I have LineString features styled by a single stroke. Some of these features represent two things, so in addition to the initial stroke color, I would like to have a parallel line next to it in a different color. This would be a no-brainer if ol/style/Stroke had an offset property like ol/style/Image does, but sadly, it does not. If I created offset geometries for those features, they wouldn't be visible from a zoomed out level. What are my options?
You can offset the geometries based on resolution
var style = function(feature, resolution) {
var colors = ['green', 'yellow', 'red'];
var width = 4;
var styles = [];
for (var line = 0; line < colors.length; line++) {
var dist = width * resolution * (line - (colors.length-1)/2);
var geom = feature.getGeometry();
var coords = [];
var counter = 0;
geom.forEachSegment(function(from, to) {
var angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
var newFrom = [
Math.sin(angle) * dist + from[0],
-Math.cos(angle) * dist + from[1]
];
var newTo = [
Math.sin(angle) * dist + to[0],
-Math.cos(angle) * dist + to[1]
];
coords.push(newFrom);
coords.push(newTo);
if (coords.length > 2) {
var intersection = math.intersect(coords[counter], coords[counter+1], coords[counter+2], coords[counter+3]);
coords[counter+1] = (intersection) ? intersection : coords[counter+1];
coords[counter+2] = (intersection) ? intersection : coords[counter+2];
counter += 2;
}
});
styles.push(
new ol.style.Style({
geometry: new ol.geom.LineString(coords),
stroke: new ol.style.Stroke({
color: colors[line],
width: width
})
})
);
}
return styles;
};
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector();
var vector = new ol.layer.Vector({
source: source,
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
map.addInteraction(new ol.interaction.Draw({
source: source,
type: 'LineString'
}));
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>
The intersect test prevents effects like this on sharp angles
For each each segment of the feature geometry the style function calculates parallel segments set to be 4 pixels apart regardless of the resolution (in this case line width and spacing are the same) for the styling geometries centered around the original segment (e.g. if three colors the center style line would follow the original and the outer style lines are offset by +/- 4, if two colors each lines would be offset by +/- 2) Initially the parallel segments are the same length as the original but need to be increased at outside angles or reduced at inside angles so math.intersect is used to find the point where they should join to form a continuous line. Finally for each color the completed linestring is used as the geometry for the color's style, and an array containing each style is returned.
Here is a second example where instead of simple colors and fixed width each offset line has its own width and stroke pattern.
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
var pointStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
});
var style = function(feature, resolution) {
var widths = [10, 10];
var strokes = [
[
{
width: 4,
color: '#7af500'
}, {
width: 4,
color: '#55aa00',
lineDash: [7,9]
}
], [
{
width: 2,
color: 'black'
}, {
width: 8,
color: 'black',
lineDash: [2,14],
lineCap: 'butt'
}
]
];
var styles = [pointStyle];
var totalWidth = 0
for (var line = 0; line < widths.length; line++) {
totalWidth += widths[line];
}
var width = 0;
for (var line = 0; line < widths.length; line++) {
var dist = (width + widths[line]/2 - totalWidth/2) * resolution;
width += widths[line];
var geom = feature.getGeometry();
if (geom.forEachSegment) {
var coords = [];
var counter = 0;
geom.forEachSegment(function(from, to) {
var angle = Math.atan2(to[1] - from[1], to[0] - from[0]);
var newFrom = [
Math.sin(angle) * dist + from[0],
-Math.cos(angle) * dist + from[1]
];
var newTo = [
Math.sin(angle) * dist + to[0],
-Math.cos(angle) * dist + to[1]
];
coords.push(newFrom);
coords.push(newTo);
if (coords.length > 2) {
var intersection = math.intersect(coords[counter], coords[counter+1], coords[counter+2], coords[counter+3]);
coords[counter+1] = (intersection) ? intersection : coords[counter+1];
coords[counter+2] = (intersection) ? intersection : coords[counter+2];
counter += 2;
}
});
strokes[line].forEach( function(stroke) {
styles.push(
new ol.style.Style({
geometry: new ol.geom.LineString(coords),
stroke: new ol.style.Stroke(stroke)
})
);
});
}
}
return styles;
};
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector();
var vector = new ol.layer.Vector({
source: source,
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
map.addInteraction(new ol.interaction.Draw({
source: source,
style: style,
type: 'LineString'
}));
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>
I have a version of this that works - but not when I zoom the chart.
Lines separates on zoom
I plot lines on the chart (from the database) and use our function to
draw the lines.
I tried to insert code here but my functions in the javascript
are not run. How do I use functions in code snipplets?
function print_my_string( my_string )
{
console.log( my_string );
}
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/5.4.1/math.min.js"></script>
<div id="map" class="map"></div>
// How do I use the java-script (Include before called...
<script>print_my_string( "Hello" );</script>
Related
I am working with openlayers 6 and I need to draw clouds on the maps a polygon with half circles stroke or a linestring, it doesn't matter the type as long as I can modify it ( adding and removing points, stretching, shrinking it). My knowledge of Openlayers is very limited so I am asking for guidance, how can I possibly do that
You could use a custom style which changes the displayed geometry, similar to https://openlayers.org/en/latest/examples/polygon-styles.html but replacing the segments of the polygon ring with semi-circular arcs.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.14.1/css/ol.css" type="text/css">
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.14.1/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector({wrapX: false});
var style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
}),
geometry: function (feature) {
var geometry = feature.getGeometry();
if (geometry.getType() === 'Polygon') {
var coordinates = geometry.getCoordinates(true)[0];
var arcs = [];
for (let i = 0, len = coordinates.length - 1; i < len; ++i) {
var center = [
(coordinates[i + 1][0] + coordinates[i][0]) / 2,
(coordinates[i + 1][1] + coordinates[i][1]) / 2
];
var dx = coordinates[i + 1][0] - coordinates[i][0];
var dy = coordinates[i + 1][1] - coordinates[i][1];
var radius = Math.sqrt(dx * dx + dy * dy) / 2;
var angle = Math.atan2(-dy, -dx);
var sides = 32;
arcs = arcs.concat(
ol.geom.Polygon.fromCircle(
new ol.geom.Circle(center, radius),
sides,
angle
).getCoordinates(true)[0].slice(0, sides / 2)
);
}
return new ol.geom.Polygon([arcs.concat([arcs[0]])]);
} else {
return geometry;
}
}
});
var vector = new ol.layer.Vector({
source: source,
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
var modify = new ol.interaction.Modify({source: source});
map.addInteraction(modify);
var draw = new ol.interaction.Draw({
source: source,
type: 'Polygon',
style: style
});
map.addInteraction(draw);
var snap = new ol.interaction.Snap({source: source});
map.addInteraction(snap);
</script>
</body>
</html>
Second example with elliptical arcs:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.14.1/css/ol.css" type="text/css">
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.14.1/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector({wrapX: false});
var style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
}),
geometry: function (feature) {
var geometry = feature.getGeometry();
if (geometry.getType() === 'Polygon') {
var coordinates = geometry.getCoordinates(true)[0];
var arcs = [];
for (let i = 0, len = coordinates.length - 1; i < len; ++i) {
var center = [
(coordinates[i + 1][0] + coordinates[i][0]) / 2,
(coordinates[i + 1][1] + coordinates[i][1]) / 2
];
var dx = coordinates[i + 1][0] - coordinates[i][0];
var dy = coordinates[i + 1][1] - coordinates[i][1];
var radius = Math.sqrt(dx * dx + dy * dy) / 2;
var angle = Math.atan2(-dy, -dx);
var sides = 32;
var circle = ol.geom.Polygon.fromCircle(
new ol.geom.Circle(center, radius),
sides,
angle
);
circle.rotate(-angle, center);
circle.scale(1, 0.5, center);
circle.rotate(angle, center);
arcs = arcs.concat(
circle.getCoordinates(true)[0].slice(0, sides / 2)
);
}
return new ol.geom.Polygon([arcs.concat([arcs[0]])]);
} else {
return geometry;
}
}
});
var vector = new ol.layer.Vector({
source: source,
style: style
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
var modify = new ol.interaction.Modify({source: source});
map.addInteraction(modify);
var draw = new ol.interaction.Draw({
source: source,
type: 'Polygon',
style: style
});
map.addInteraction(draw);
var snap = new ol.interaction.Snap({source: source});
map.addInteraction(snap);
</script>
</body>
</html>
I am trying to create a circle based on the point/coordinate a user clicks. I know how to create a point and a found a function to create a circle based on the point (like a buffer/range ring) but it seems to only work with x,y points (0,0). I tried converting my lon and lat coordinates to X and Y using ol.proj.transform but it doesn't rendered a circle at all.
Link with the function to create circle
This is what I am trying to create
function createCircle(circleCenterX, circleCenterY, circleRadius, pointsToEnd) {
let angleToAdd = 360 / pointsToEnd;
let coords = [];
let angle = 0;
for (let i = 0; i < pointsToEnd; i++) {
angle += angleToAdd;
let coordX = circleCenterX + circleRadius * Math.cos(angle * Math.PI / 180);
let coordY = circleCenterY + circleRadius * Math.sin(angle * Math.PI / 180);
coords.push([coordX, coordY]);
}
return coords;
}
function addMarker(coordinates) {
console.log(coordinates);
var marker = new ol.Feature(new ol.geom.Point([708683.3598450683, 1850098.1965979263]));
marker.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: 'red'
})
})
}));
vectorSource.addFeature(marker);
}
function addCircle(coords) {
// var lonlat1 = ol.proj.transform([coords[0], coords[1]], 'EPSG:4326','EPSG:3857');
// console.log('var lonlat1',lonlat1)
var circleCoords = createCircle(708683.3598450683, 1850098.1965979263, 20, 180);
console.log(circleCoords);
var polygon = new ol.geom.Polygon([circleCoords]);
polygon.transform('EPSG:4326', 'EPSG:3857');
polygon = new ol.Feature(polygon);
vectorSource.addFeature(polygon);
}
jsfiddle
Your problem is the addMarker function takes coordinates in the EPSG:3857 projection, the addCircle function takes them in the EPSG:4326 projection.
If you want to pass in the same coordinates, you have to make them consistent.
The circle doesn't appear for [708683.3598450683, 1850098.1965979263] because that is way off the map (the maximum value for latitude is 90 degrees).
addCircle(ol.proj.toLonLat([708683.3598450683, 1850098.1965979263]));
addMarker([708683.3598450683, 1850098.1965979263]);
updated fiddle with the same center (but in different projections)
code snippet:
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([0, 0]),
zoom: 3
})
});
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
projection: 'EPSG:4326',
features: []
}),
});
map.addLayer(layer);
var vectorSource = layer.getSource();
function createCircle(circleCenterX, circleCenterY, circleRadius, pointsToEnd) {
let angleToAdd = 360 / pointsToEnd;
let coords = [];
let angle = 0;
for (let i = 0; i < pointsToEnd; i++) {
angle += angleToAdd;
let coordX = circleCenterX + circleRadius * Math.cos(angle * Math.PI / 180);
let coordY = circleCenterY + circleRadius * Math.sin(angle * Math.PI / 180);
coords.push([coordX, coordY]);
}
return coords;
}
function addMarker(coordinates) {
console.log(coordinates);
var marker = new ol.Feature(new ol.geom.Point(coordinates));
marker.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: 'red'
})
})
}));
vectorSource.addFeature(marker);
}
function addCircle(coords) {
// var lonlat1 = ol.proj.transform([0, 0], 'EPSG:4326','EPSG:3857');
// console.log('var lonlat1',lonlat1)
var circleCoords = createCircle(coords[0], coords[1], 20, 180);
console.log(circleCoords);
var polygon = new ol.geom.Polygon([circleCoords]);
polygon.transform('EPSG:4326', 'EPSG:3857');
polygon = new ol.Feature(polygon);
vectorSource.addFeature(polygon);
}
addCircle(ol.proj.toLonLat([708683.3598450683, 1850098.1965979263]));
addMarker([708683.3598450683, 1850098.1965979263]);
html,
body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
.map {
height: 100%;
width: 100%;
}
<script src="https://openlayers.org/en/v6.4.3/build/ol.js"></script>
<link rel="stylesheet" type="text/css" href="https://openlayers.org/en/v6.4.3/css/ol.css" />
<div id="map" class="map"></div>
In my app I wrote an ol.interaction.Draw code that allow me to draw a circle everytime I click on one map panel, and this circle work good for me because I can move, rotate and rescale proportionally it. This is my code:
map.addInteraction(new ol.interaction.Modify({
features: this.features,
deleteCondition: function (event) {
return ol.events.condition.shiftKeyOnly(event) && ol.events.condition.singleClick(event);
}
}));
this.draw = new ol.interaction.Draw({
features: this.features,
type: 'Circle',
draggable:true;
});
this.draw.on('drawstart', function () {
this.features.clear();
}, this);
this.map.addInteraction(this.draw);
But I would like to draw an image (e.g. with the source media/image/landscape.png), instead of one circle, but with the same features (drag and drop, rotate, rescale proportionally). How I could do it?
You would probably want to draw circles but style them using your png as an icon. Scaling would be based on the circle radius. Circle geometry doesn't include rotation but by using a geometryFunction in the interaction you could set a rotation and use that to rotate the icon (the angle needs to be adjusted depending on which edge or corner of the icon is used for the rotation).
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
styles = [
new ol.style.Style({
fill: new ol.style.Fill({
color: [255, 255, 255, 0.5]
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: white,
width: width + 2
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: blue,
width: width
})
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
})
];
var treeStyle = new ol.style.Style({
image: new ol.style.Icon({
src: 'https://www.freeiconspng.com/uploads/oak-tree-icon-png-17.png'
})
});
styleFunction = function(feature, resolution) {
if (feature.getGeometry().getCenter) {
treeStyle.setGeometry(new ol.geom.Point(feature.getGeometry().getCenter()));
treeStyle.getImage().setRotation(feature.getGeometry().get('rotation'));
treeStyle.getImage().setScale(feature.getGeometry().getRadius()/(150*resolution));
return treeStyle;
} else {
return styles;
}
}
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector({wrapX: false});
var vector = new ol.layer.Vector({
source: source,
style: styleFunction
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
var draw = new ol.interaction.Draw({
source: source,
type: 'Circle',
geometryFunction: function(coordinates, geometry) {
var center = coordinates[0];
var last = coordinates[1];
var dx = center[0] - last[0];
var dy = center[1] - last[1];
var radius = Math.sqrt(dx * dx + dy * dy);
var rotation = Math.PI - Math.atan2(dy, dx);
geometry = geometry || new ol.geom.Circle(center, radius);
geometry.setCenter(center);
geometry.setRadius(radius);
geometry.set('rotation', rotation);
return new ol.geom.Circle(center, radius);
},
style: styleFunction
});
map.addInteraction(draw);
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>
I am working on openlayers, there is an issue when I try to add a linestring on the map which crosses the dateline several times.
To display the linestring across two worlds in the continuous segment I have added a function HandleDateline() which returns me modified (shifted either to left or right world) coordinates.
However, when you zoom-in on the left world to current settled view the linestring disappears. Also if you try to move the map to left, the line goes away.
The weird thing is if the line crosses more than 1 time, linestring on left worlds disappears otherwise same happens for right worlds. Too observe that, remove either first 3 or last 3 points from datelinecrossing[] which I will be posting down.
I am expecting a continuous linestring crossing the international dateline without any issues like disappearing.
If there is a better approach to this, I am open to all ideas.
Here is the bin : ol dateline problem
You have to split the lines crossing the dateline programmatically at the dateline.
var points = [
[-170, -10],
[170, 0],
[-170, 10]
];
var vectorSource = new ol.source.Vector();
vectorSource.addFeature(createFeature(points));
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
width: 2,
color: "red"
})
})
});
var osmLayer = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
layers: [osmLayer, vectorLayer],
target: document.getElementById("map"),
view: new ol.View({
center: ol.proj.transform([180, 0], "EPSG:4326", "EPSG:3857"),
zoom: 3
})
});
var graticule = new ol.Graticule({
strokeStyle: new ol.style.Stroke({
color: "rgba(255,120,0,0.9)",
width: 1.5,
lineDash: [0.5, 4]
}),
showLabels: true
});
graticule.setMap(map);
// -------------------------------------------------
function createFeature(points) {
var pointsSplitted = [];
var pointsArray = [];
pointsSplitted.push(points[0]);
var lastLambda = points[0][0];
for (var i = 1; i < points.length; i++) {
var lastPoint = points[i - 1];
var nextPoint = points[i];
if (Math.abs(nextPoint[0] - lastLambda) > 180) {
var deltaX = xToValueRange(nextPoint[0] - lastPoint[0]);
var deltaY = nextPoint[1] - lastPoint[1];
var deltaXS = xToValueRange(180 - nextPoint[0]);
var deltaYS;
if (deltaX === 0) {
deltaYS = 0;
} else {
deltaYS = deltaY / deltaX * deltaXS;
}
var sign = lastPoint[0] < 0 ? -1 : 1;
pointsSplitted.push([180 * sign, nextPoint[1] + deltaYS]);
pointsArray.push(pointsSplitted);
pointsSplitted = [];
pointsSplitted.push([-180 * sign, nextPoint[1] + deltaYS]);
}
pointsSplitted.push(nextPoint);
lastLambda = nextPoint[0];
}
pointsArray.push(pointsSplitted);
var geom = new ol.geom.MultiLineString(pointsArray);
geom.transform("EPSG:4326", "EPSG:3857");
var feature = new ol.Feature({
geometry: geom
});
return feature;
}
function xToValueRange(x) {
if (Math.abs(x) > 180) {
var sign = x < 0 ? -1 : 1;
return x - 2 * 180 * sign;
} else {
return x;
}
}
html,
body,
#map {
width: 100%;
height: 100%;
overflow: hidden
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<body>
<div id="map" class="map" tabindex="0"></div>
</body>
Simplifying HandleDateline so all coordinates are either in the western part of the normal world or the eastern part of the left world seems to fix it (so if the geometry crosses the date line the extent starts in the left world)
function HandleDateline(array) {
for (var i = 0; i < array.length ; i++) {
if (array[i][0] > 0) {
array[i][0] -= 360;
}
}
}
However in worlds to the right the points west of the dateline then appear to be rendered below to linestring, while those east of the dateline are above it. Moving HandleDateline(datelinecrossing); above datelinecrossing.forEach fixes that.
You might also want to consider using a multipoint geometry for the points (unless you need them to be individually selectable).
HandleDateline(datelinecrossing);
var pdlcrossing = new ol.Feature({
geometry: new ol.geom.MultiPoint(datelinecrossing).transform('EPSG:4326', 'EPSG:3857')
});
drawingSource.addFeature(pdlcrossing);
var dlcrossing = new ol.Feature({
geometry: new ol.geom.LineString(datelinecrossing).transform('EPSG:4326', 'EPSG:3857')
});
drawingSource.addFeature(dlcrossing);
There is still a problem zooming in west of the dateline in the worlds on the right, so I think there will need to be two sets of each geometry, one offset to the left and another 360 degrees to the right:
function PlotGeometries(datelinecrossing){
var pgeom = new ol.geom.MultiPoint(datelinecrossing);
var pdlcrossing = new ol.Feature({
geometry: pgeom.clone().transform('EPSG:4326', 'EPSG:3857')
});
drawingSource.addFeature(pdlcrossing);
pgeom.translate(360,0);
var pdlcrossing2 = new ol.Feature({
geometry: pgeom.transform('EPSG:4326', 'EPSG:3857')
});
drawingSource.addFeature(pdlcrossing2);
var geom = new ol.geom.LineString(datelinecrossing);
var dlcrossing = new ol.Feature({
geometry: geom.clone().transform('EPSG:4326', 'EPSG:3857')
});
drawingSource.addFeature(dlcrossing);
geom.translate(360,0);
var dlcrossing2 = new ol.Feature({
geometry: geom.transform('EPSG:4326', 'EPSG:3857')
});
drawingSource.addFeature(dlcrossing2);
}
Just set an extent to the map view for the part of the world you're working on.
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
});