I need to run Graticule labels in OpenLayers 3. I want to show and style labels of graticule with angle units. I just found this :
https://github.com/Brictarus/ol3/blob/d41eb87204e76cbf99d61915eb89b1c16c4a4e05/examples/graticule.js
But I cant get it works in my html :
var polohaMysky = new ol.control.MousePosition({
coordinateFormat: ol.coordinate.createStringXY(4),
//projection: "EPSG:4326",
className: "suradnice",
target: document.getElementById("suradnice_div"),
undefinedHTML: '0000'
})
//------//
var mierka = new ol.control.ScaleLine();
var nahlad = new ol.control.OverviewMap();
var a1 = new ol.source.OSM();
var map = new ol.Map({
renderer: 'canvas',
// Zobrazenie súradníc myšky
controls: ol.control.defaults({
attributions: false
}).extend([polohaMysky, mierka, nahlad]),
layers: [
new ol.layer.Tile({
source: a1
})
],
target: 'map',
view: new ol.View({
//projection: 'EPSG:4326',
center: ol.proj.fromLonLat([4.8, 47.75]),
zoom: 3
})
});
var lonFormatter = function(lon) {
var formattedLon = Math.abs(Math.round(lon * 100) / 100);
formattedLon += (lon < 0) ? 'W' : ((lon > 0) ? 'E' : '');
return formattedLon;
};
var latFormatter = function(lat) {
var formattedLat = Math.abs(Math.round(lat * 100) / 100);
formattedLat += (lat < 0) ? 'S' : ((lat > 0) ? 'N' : '');
return formattedLat;
};
// Create the graticule component
var graticule = new ol.Graticule({
// the style to use for the lines, optional.
strokeStyle: new ol.style.Stroke({
color: 'rgba(255,120,0,0.9)',
width: 2,
lineDash: [0.5, 4]
}),
showLabels: true,
lonLabelFormatter: lonFormatter,
lonLabelPosition: 0.05,
latLabelFormatter: latFormatter,
latLabelPosition: 0.95
});
graticule.setMap(map);
When i run this code i just see only graticule without labels. Have you any idea what is wrong ? Thanks for any help.
From latest document , seems like OL3 is not support label yet
Graticule labels are not supported in OpenLayers 3 (latest version of OL3 is 3.20.1). See OL3 API reference here: https://openlayers.org/en/v3.20.1/apidoc/ol.Graticule.html
If you need to display graticule labels consider upgrading to OpenLayers 5, which does support graticule labels. See OL5 API reference here: https://openlayers.org/en/latest/apidoc/module-ol_Graticule.html
Related
I need to implement a measure tool with OpenLayers, and I would like to display distance on the segment, with a smart management of the scale (a mark on the segment each 10m, then each 50m, 100m, 1km, 5km, for example...), very much like the GoogleMaps "measure distance" tool.
Is there any library doing that ? What would be the good approach to implement it ?
In short: No, I don't know any lib or class that provides what you want out of the box.
Option 1: Customize ScaleLine
ScaleLine (see api docs) has the option to provide your own render function (see code). The default implementation just calculates the distance and shows it as {number} {scale} by calling the internal function updateElement_, which then updates the ScaleLine's innerHtml.
You could theoretically replace that method and set the innerHTML yourself. That approach might limit you to the development-variant of the library, because the production code is minified and those elements (innerElement_, element_) are not marked as api.
new ol.control.ScaleLine({
render: function(mapEvent) {
// do stuff
}
});
Option 2: Use the Draw Feature with customized LineString styles
so that might be too complicated and I suggest you go for the ol.interaction.Draw feature. The Measure Example shows us how one could draw stuff while the user is drawing a line. You can combine that with custom styles on a LineString.
// TODO split the uses drawn line into segments, like this mockup
const line = new ol.geom.LineString([
[20.0, 50.0],
[30.0, 47.0],
[40.0, 47.0],
[50.0, 47.0]
]);
line.transform('EPSG:4326', 'EPSG:3857');
const lineFeature = new ol.Feature(line);
const lineSource = new ol.source.Vector({
features: [lineFeature]
});
function segmentText(coord, coord2) {
const coord_t = ol.proj.transform(coord, 'EPSG:3857', 'EPSG:4326');
let coordText = coord_t[1].toFixed(0) + '/' + coord_t[0].toFixed(0);
if(coord2) {
const length = ol.Sphere.getLength(new ol.geom.LineString([coord2, coord]));
const distance = (Math.round(length / 1000 * 100) / 100) + ' km';
coordText = coordText + '\n' + distance;
} else {
coordText = coordText + '\n0';
}
return new ol.style.Text({
text: coordText,
fill: new ol.style.Fill({
color: "#00f"
}),
offsetY: 25,
align: 'center',
scale: 1,
});
}
function styleFunction(feature) {
var geometry = feature.getGeometry();
var styles = [
// linestring style
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 2
})
})
];
function createSegmentStyle(coord, coord2, rotation) {
return new ol.style.Style({
geometry: new ol.geom.Point(coord),
image: new ol.style.Icon({
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAIAAADdv/LVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAUSURBVBhXY1Da6MPEwMDAxMDAAAALMAEkQYjH8gAAAABJRU5ErkJggg==',
anchor: [0.75, 0.5],
rotateWithView: true,
rotation: -rotation,
scale: 4
}),
text: segmentText(coord, coord2)
})
};
const firstCoord = geometry.getFirstCoordinate();
geometry.forEachSegment(function (start, end) {
var dx = end[0] - start[0];
var dy = end[1] - start[1];
var rotation = Math.atan2(dy, dx);
if (firstCoord[0] === start[0] && firstCoord[1] === start[1]) {
styles.push(createSegmentStyle(start, null, rotation));
}
styles.push(createSegmentStyle(end, firstCoord, rotation));
});
return styles;
}
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.Stamen({ layer:'toner-lite' })
}),
new ol.layer.Vector({
source: lineSource,
style: styleFunction
})
],
view: new ol.View({
center: ol.proj.transform(
[35, 45], 'EPSG:4326', 'EPSG:3857'),
zoom: 4
})
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.5.0/ol.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.5.0/ol-debug.js"></script>
<div id="map" style="height:300px"></div>
I was recently tasked with creating the map for my Wurm Online game alliance. I crafted a genius SVG-based overlay over a static image of an in-game map. Basically it takes data from a spreadsheet and renders Villages as colored circles on the map.
However, we have members all the over the map, so went about to seeing how I could create a zoom-able web-based map of our lands. The game admins give us a map dump every year or thereabouts, so we can create custom map applications however we feel. I downloaded the recent map dump for the island/server I care about, Xanadu.
The Xanadu dump is a 62MB PNG with a resolution of 8192 x 8192 pixels. I found a tile making program (MapTiler version 7), and I went about creating tiles. After the tiles are done rendering, the program itself creates HTML files with embedded JavaScript all programatically. It gave me a head start with OpenLayers3.
I was able to re-calculate Village coordinates and cobble together a zoom-able tiled map with Village circles. Needless to say, I was very happy when I got my custom OpenLayers3 map. (Working example: http://jackswurmtools.com/Content/Static/map.html)
The way I got it set up, each map "decoration" or colored circle is its own Vector.
The chief complaint from my fellow gamers about my zoom-able map, is that the color Village circles are too big zoomed out, yet too small when zoomed in.
I've tried all kinds of things, but I have yet to find the right examples. I'm used to finding and transforming SVG elements based on events, but the OP3 canvas rendering is NOT obvious to me in the DOM.
Some of my questions are:
How can I detect when my map has been zoomed? Is there some callback I'm missing?
And when a zoom is detected, how can I iterate through all my vectors and update a circle radius.
// jacks fully zoomable xanadu map
var data =
[{
X: "6744",
Y: "-2355.75",
Name: "Amish Creek",
Villagers: ["Aniceset", "Fulano"],
BackColor: "Aquamarine",
LandMarkType: "Member"
}, {
X: "6808.75",
Y: "-2265.125",
Name: "Amish Estates",
Villagers: ["Aniceset", "Villagers"],
BackColor: "Purple",
LandMarkType: "Member"
}];
console.log(data);
var mapExtent = [0.00000000, -8192.00000000, 8192.00000000, 0.00000000];
var mapMinZoom = 0;
var mapMaxZoom = 5;
var mapMaxResolution = 1.00000000;
var tileExtent = [0.00000000, -8192.00000000, 8192.00000000, 0.00000000];
var mapResolutions = [];
for (var z = 0; z <= mapMaxZoom; z++) {
mapResolutions.push(Math.pow(2, mapMaxZoom - z) * mapMaxResolution);
}
var mapTileGrid = new ol.tilegrid.TileGrid({
extent: tileExtent,
minZoom: mapMinZoom,
resolutions: mapResolutions
});
var features = [];
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
projection: 'PNGMAP',
tileGrid: mapTileGrid,
url: "http://jackswurmtools.com/Content/Static/{z}/{x}/{y}.png"
})
}),
],
view: new ol.View({
zoom: 4,
center: [6602.375, -2250.3125],
projection: ol.proj.get('PNGMap'),
maxResolution: mapTileGrid.getResolution(mapMinZoom)
}),
});
map.getView();
map.on('singleclick', function(evt) {
var coord = evt.coordinate;
console.log(coord);
$("#coord-overlay").html("[" + coord[0] + ", " + coord[1] + "]");
});
// zoom stuff?
// add layers via JSON iteration
renderSVG(data);
drawLines();
function renderSVG(data) {
var vectorSource = new ol.layer.Vector({});
console.log(map.getView().getZoom());
jQuery.each(data, function() {
var fill = new ol.style.Fill({
color: this.BackColor
});
var stroke = new ol.style.Stroke({
color: [180, 0, 0, 1],
width: 1
});
var style = new ol.style.Style({
image: new ol.style.Circle({
fill: fill,
stroke: stroke,
radius: map.getView().getZoom() * 5,
opacity: 0.7
}),
fill: fill,
stroke: stroke,
text: new ol.style.Text({
font: '12px helvetica,sans-serif',
text: this.Name,
fill: new ol.style.Fill({
color: '#000'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
});
var point_feature = new ol.Feature({});
var point_geom = new ol.geom.Point([this.X, this.Y]);
point_feature.setGeometry(point_geom);
var vector_layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [point_feature],
})
});
vector_layer.setStyle(style);
map.addLayer(vector_layer);
});
}
function drawLines() {
var stroke = new ol.style.Stroke({
color: [255, 0, 0, 1],
width: 6
});
var style = new ol.style.Style({
fill: null,
stroke: stroke,
text: new ol.style.Text({
font: '12px helvetica,sans-serif',
text: "Sandokhan / Wargasm Canal",
fill: new ol.style.Fill({
color: '#000'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
});
var line_feature = new ol.Feature({});
var coords = [
[6607.5, -1921],
[6894, -1921]
];
var layerLines = new ol.layer.Vector({
source: new ol.source.Vector({
features: [new ol.Feature({
geometry: new ol.geom.LineString(coords, 'XY'),
name: 'Line',
})]
})
});
layerLines.setStyle(style);
map.addLayer(layerLines);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.8.2/ol.min.css" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.8.2/ol.min.js" type="text/javascript"></script>
<div id="map"></div>
<div id="coord-overlay">[6612, -2252]</div>
<input id="slider" type="range" min="0" max="1" step="0.1" value="1" oninput="layer.setOpacity(this.value)">
You are looking for a resolution listener, the API docs is an excellent place:
map.getView().on('change:resolution', function(){
var zoom = map.getView().getZoom();
// your conditions
if (zoom < 10) {
// your ol.layer.Vector assuming `vector_layer` global variable
vector_layer.setStyle(style_with_another_radius);
}
});
you can listen to the moveend event for the zoom, however it is also fired when you move the map:
map.on('moveend', function(){
var radius= map.getView().getZoom() * someFactor; // or whatever +,/ ...
yourVectorVillage.setStyle(new ol.style.Circle({ radius : radius}));
// or a style of your own where you can modify the radius
});
Is it possible in OpenLayers 3 to create a text label which clones multiple times along a linestring feature, depending on a scale? Something like:
Here you can see, that when we change scale label "IX Corps Blvd" appears twice. How can we implement this?
You can achieve this with style function. My code sample is about making arrows to line string (slightly different case), but I have commented parts necessary to be changed (at least):
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector();
var styleFunction = function(feature) {
var geometry = feature.getGeometry();
var styles = [
// linestring
new ol.style.Style({
stroke: new ol.style.Stroke({ // apply street style here
color: '#ffcc33',
width: 2
})
})
];
geometry.forEachSegment(function(start, end) {
var dx = end[0] - start[0];
var dy = end[1] - start[1];
var rotation = Math.atan2(dy, dx);
// arrows
styles.push(new ol.style.Style({
geometry: new ol.geom.Point(end),
image: new ol.style.Icon({ // Use here label, not icon.
src: 'http://openlayers.org/en/v3.17.1/examples/data/arrow.png',
anchor: [0.75, 0.5],
rotateWithView: false,
rotation: -rotation
})
}));
});
return styles;
};
var vector = new ol.layer.Vector({
source: source,
style: styleFunction
});
map.addInteraction(new ol.interaction.Draw({
source: source,
type: /** #type {ol.geom.GeometryType} */ ('LineString')
}));
Some more effort is needed to place titles in correct placements. I left this answer like this to serve a solid starting point for building your feature.
My source:
[1] http://openlayers.org/en/master/examples/line-arrows.html
I was planning to draw polygon from lat long values referring this code.
open layers 3 how to draw a polygon programmically?
var polyCoordinates =[[1.300910900488229, 44.28372648003116],[1.3373031124022878, 44.13311552125895],[1.6648330196289067, 44.12030076099872]];
var polygon = new OpenLayers.Geometry.Polygon([polyCoordinates]);
// Create feature with polygon.
var feature = new OpenLayers.Feature(polygon);
// Create vector source and the feature to it.
var vectorSource = new OpenLayers.source.Vector();
// Create the vectorial layer
var vectorLayer = new OpenLayers.Layer.Vector('Vector Layer', {
source: vectorSource
styleMap: new OpenLayers.StyleMap({
'default': OpenLayers.Util.applyDefaults({
strokeWidth: 3,
graphicName: 'triangle',
pointRadius: '${radius}',
rotation: '${angle}'
}, OpenLayers.Feature.Vector.style['default']
),
'select': OpenLayers.Util.applyDefaults({
pointRadius: '${radius}'
}, OpenLayers.Feature.Vector.style['select']
)
})
});
OpenLayers.source is undefined is showing as error. Any help would be appreciated.
var sitePoints = [];
var coordinates =[{"long":1.300910900488229,"lat":44.28372648003116},
{"long":1.3373031124022878,"lat":44.13311552125895},
{"long":1.6648330196289067,"lat":44.12030076099872}];
var siteStyle = {
label:'ring',
title: 'press to close as a ring',
cursor: "pointer",
fontSize: '8px',
fontColor: '#222',
pointRadius: 10,
fillColor: '#cccccc',
strokeColor: '#444444'
};
var epsg4326 = new OpenLayers.Projection("EPSG:4326");
for (var i in coordinates) {
var coord = coordinates[i];
var point = new OpenLayers.Geometry.Point(coord.long, coord.lat);
// transform from WGS 1984 to Spherical Mercator
point.transform(epsg4326, map.getProjectionObject());
sitePoints.push(point);
}
console.log(sitePoints);
var linearRing = new OpenLayers.Geometry.LinearRing(sitePoints);
var geometry = new OpenLayers.Geometry.Polygon([linearRing]);
var polygonFeature = new OpenLayers.Feature.Vector(geometry, null, siteStyle);
vectorLayer.addFeatures([polygonFeature]);
map.addLayer(vectorLayer);
Here is a proper way to do this in OpenLayers 3, which has a different API than previous versions.
var polyCoordinates =[
[1.300910900488229, 44.28372648003116],
[1.3373031124022878, 44.13311552125895],
[1.6648330196289067, 44.12030076099872]];
var polygon = new ol.geom.Polygon([polyCoordinates]);
polygon.applyTransform(ol.proj.getTransform('EPSG:4326', 'EPSG:3857'));
// Create feature with polygon.
var feature = new ol.Feature({
geometry: polygon
});
// Create vector source and the feature to it.
var vectorSource = new ol.source.Vector({
features: [feature]
});
// Create the vectorial layer
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#eedd00',
width: 3
})
})
});
Edit: If you stick with OpenLayers 2, you need to create the polygon via linear ring. The setup is a little bit different. There is no OpenLayers source to deal with.
var polyCoordinates =[
[1.300910900488229, 44.28372648003116],
[1.3373031124022878, 44.13311552125895],
[1.6648330196289067, 44.12030076099872]];
// Create the polygon via linear ring
var points = [];
for (var i = 0; i < polyCoordinates.length; i++) {
coord = polyCoordinates[i];
points.push(new OpenLayers.Geometry.Point(coord[0], coord[1]));
}
var linearRing = new OpenLayers.Geometry.LinearRing(points);
var polygon = new OpenLayers.Geometry.Polygon([linearRing]);
var srcProj = new OpenLayers.Projection("EPSG:4326");
var targetProj = new OpenLayers.Projection("EPSG:3857");
polygon.transform(srcProj, targetProj);
// Create feature with polygon.
var feature = new OpenLayers.Feature.Vector(polygon);
// Create the vectorial layer
var vectorLayer = new OpenLayers.Layer.Vector('Vector Layer',
OpenLayers.Feature.Vector.style['default']
);
vectorLayer.addFeatures([feature]);
Does anybody know how to get a count for the number of features present in an OL3 vector layer?
My vector layer is defined as follows, I'd like to know how many elements it has, and ideally how many are currently being rendered:
var styleCache = {};
var WFS_layer_Traffic_Lights = new ol.layer.Vector({
source : new ol.source.GeoJSON({
projection : 'EPSG:3857',
url : "Vector_Data/Traffic_Lights_Bordeaux.geojson"
}),
style : function(feature, resolution) {
var path;
var x_anchor;
var y_anchor;
if(resolution < 4){
path = 'Icons/Traffic_Lights_Sign_Icon_Small.png';
x_anchor = 23;
y_anchor = 90;
}
if(resolution >= 4 && resolution < 10){
path = 'Icons/Traffic_Lights_Sign_Small.png';
x_anchor = 16;
y_anchor = 16;
}
if(resolution >= 10){
path = 'Icons/Traffic_Lights_Sign_Tiny.png';
x_anchor = 10;
y_anchor = 10;
}
if (!styleCache[path]) {
styleCache[path] = [new ol.style.Style({
fill : new ol.style.Fill({
color : 'rgba(255, 255, 255, 0.1)'
}),
stroke : new ol.style.Stroke({
color : '#319FD3',
width : 1
}),
image: new ol.style.Icon(({
anchor: [x_anchor, y_anchor],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
src: path
})),
text : new ol.style.Text({
font : '12px Calibri,sans-serif',
text : "",
fill : new ol.style.Fill({
color : '#000'
}),
stroke : new ol.style.Stroke({
color : '#fff',
width : 4
})
}),
zIndex : 1
})];
}
return styleCache[path];
}
});
Once the (GeoJSON) features are loaded you can call getFeatures on the vector source to get an array with references to the features included in the vector source. So, to get the number of features you can use the following:
var featureCount = vectorLayer.getSource().getFeatures().length;
As stated above the source should be loaded for this to work. If you pass a url option to the source constructor the source will use an Ajax request to download features. This occurs asynchronously, meaning that the source won't contain features after construction.
You can register a change listener on the vector source to know when it's loaded:
var vectorSource = vectorLayer.getSource();
var listenerKey = vectorSource.on('change', function(e) {
if (vectorSource.getState() == 'ready') {
var featureCount = vectorSource.getFeatures().length;
// ...
ol.Observable.unByKey(listenerKey);
// use vectorSource.unByKey(listenerKey) instead
// if you do use the "master" branch of ol3
}
});
EDIT: I edited this to change from change:state to change as the event name.