OpenLayers 4 zoom to cluster - javascript

Im using OpenLayers 4 and have a layer with an ol.source.Cluster as source. When I click on a clustered point I want to zoom in to the extent of the original points that makes up the cluster. My problem is that I can not find these original points anywhere.
I have tried to calculate an extent from the distance I use on the cluster but the result is not satisfying.
Any idea how to determine which the original points behind a cluster point are?
<html>
<head>
<title>Cluster</title>
<link rel="stylesheet" href="https://openlayers.org /en/v4.1.1/css/ol.css" type="text/css">
<script src="https://openlayers.org/en/v4.1.1/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var geojsonObject = {
'type': 'FeatureCollection',
'crs': {
'type': 'name',
'properties': {
'name': 'EPSG:3857'
}
},
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [0, 0]
}
}, {
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [9, 9]
}
},{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [10, 10]
}
}]
};
var source = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(geojsonObject)
});
var clusterSource = new ol.source.Cluster({
source: source,
distance: 30
});
var layer = new ol.layer.Vector({
source: clusterSource
});
var select = new ol.interaction.Select({
layers: [layer]
});
var view = new ol.View({
center: [5, 5],
zoom: 20
});
var map = new ol.Map({
target: 'map',
layers: [new ol.layer.Tile({
source: new ol.source.OSM()
}),layer],
view: view
});
map.addInteraction(select);
var selectedFeatures = select.getFeatures();
selectedFeatures.on('add', function(event) {
// event.target only contains the clustered point
var feature = event.target.item(0);
});
</script>
</body>
</html>

I found the answer myself in a OpenLayers example.
In the selected feature function you can do
var originalFeatures = feature.get('features');
To get the original features, and then in my case to zoom to the extent
var extent = new ol.extent.createEmpty();
originalFeatures.forEach(function(f, index, array){
ol.extent.extend(extent, f.getGeometry().getExtent());
});
map.getView().fit(extent, map.getSize());

You can get the features in the cluster from:
source.getFeatures()
You can get the extent from:
source.getExtent()
Where source is an instance of ol.source.Cluster

Related

translate MapLibre to openlayer's

Can someone help me translate the code below MapLibre to openlayer's?
map.addSource('source', {
'type': 'vector',
'tiles': ['url'],
'minzoom': 6,
'maxzoom': 14
});
In Openlayers there are many ways to load a vector layer, one way for a wfs service can look like this
var vectorSource = new ol.source.Vector({
format : 'wfsFormat',
loader: 'function'
strategy: ol.loadingstrategy.*,
});
var vectorLayer= new ol.layer.Vector({
title: 'title',
visible: true,
source: vectorSource,
projection: 'EPSG:4326',
style: 'function'
});
map.addLayer(vectorLayer);

Mapbox GL snap GPS coordinates do road

I use Mapbox to show on map the path that the user traveled (with my app on phone). My application saves the GPS position regularly, which I then show on the map with the help of Mapbox GL.
<script>
mapboxgl.accessToken = 'pk.XXX';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [aa.AAA, bb.BBB],
zoom: 18
});
var popup = new mapboxgl.Popup()
.setText("XXX YYY")
.addTo(map);
var marker = new mapboxgl.Marker()
.setLngLat([aa.AAA, bb.BBB])
.addTo(map)
.setPopup(popup);
map.on('load', function () {
map.addSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': [
[xx.XXX,yy.YYY],
[xx.XXX,yy.YYY],
[xx.XXX,yy.YYY]
}
}
});
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#888',
'line-width': 8
}
});
});
</script>
I would like Mapbox to snap the position to the road on the map. Something like "snap-to-road" in Google Maps.
I know that Mapbox has something called "map matching". Only I want to list many intermediate points (not just the beginning and the end), and I would like everyone with them to be snaped to the road on the map.
Is something like this possible? Maybe there are some other mapping solutions that can do that?
Take a look at my solution below. It uses Mapbox map matching, then extracts the tracepoints from the response object and draws them on the map.
To make it work on your end, just replace <ACCESS_TOKEN> with your Mapbox access token.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Map Matched Coordinates</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = '<ACCESS_TOKEN>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-117.17292,32.71256],
zoom: 15
});
map.on('load', function () {
// map matching request
var matched_coordinates = [];
// Create
let xhr = new XMLHttpRequest();
// GET REQUEST
xhr.open('GET', 'https://api.mapbox.com/matching/v5/mapbox/driving/' +
'-117.17282,32.71204;-117.17288,32.71225;-117.17293,32.71244;-117.17292,32.71256;-117.17298,32.712603;-117.17314,32.71259;-117.17334,32.71254' +
'?access_token=<ACCESS_TOKEN>' +
'&geometries=geojson');
// After Response is received
xhr.onload = function() {
if (xhr.status != 200) {
// NOT SUCCESSFUL
} else {
// SUCCESSFUL
var response = JSON.parse(xhr.response);
var tracepoints = response["tracepoints"];
for(var i = 0; i < tracepoints.length; i++){
matched_coordinates.push(tracepoints[i]["location"]);
}
map.addSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': matched_coordinates
}
}
});
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#888',
'line-width': 8
}
});
}
}
xhr.send();
});
</script>
</body>
</html>

Edit marker size and popup box size

I managed to get the marker and the popup box to appear in the Mapbox API
However, I don't seem to be able to edit their size and popup size.
for (let i = 0; i < locations.length; i++) {
let location = locations[i];
let marker = new mapboxgl.Marker({ color: '#FF8C00' });
marker.setLngLat(location.coordinates);
let popup = new mapboxgl.Popup({ offset: 40 });
popup.setHTML(location.description);
marker.setPopup(popup);
// Display the marker.
marker.addTo(map);
// Display the popup.
popup.addTo(map);
}
Mapbox ususally recommends to use the Annotation Plugin to create markers and interact with these.
However, if you would like to stick to the example you have presented, you could follow this Mapbox example in which custom icons are used for the marker symbol. The size of these icons can be adjusted with the iconSize property:
<script>
mapboxgl.accessToken = 'access_token';
var geojson = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {
'message': 'Foo',
'iconSize': [60, 60]
},
'geometry': {
'type': 'Point',
'coordinates': [-66.324462890625, -16.024695711685304]
}
},
{
'type': 'Feature',
'properties': {
'message': 'Bar',
'iconSize': [50, 50]
},
'geometry': {
'type': 'Point',
'coordinates': [-61.2158203125, -15.97189158092897]
}
},
{
'type': 'Feature',
'properties': {
'message': 'Baz',
'iconSize': [40, 40]
},
'geometry': {
'type': 'Point',
'coordinates': [-63.29223632812499, -18.28151823530889]
}
}
]
};
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-65.017, -16.457],
zoom: 5
});
// add markers to map
geojson.features.forEach(function(marker) {
// create a DOM element for the marker
var el = document.createElement('div');
el.className = 'marker';
el.style.backgroundImage =
'url(https://placekitten.com/g/' +
marker.properties.iconSize.join('/') +
'/)';
el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
el.addEventListener('click', function() {
window.alert(marker.properties.message);
});
// add marker to map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);
});
</script>

Creating circle with color gradient without using heatmap

I have managed to get a circle but can't seem to apply a gradient here. The centermost has to be the darkest color reducing thus on. Please suggest the code to be added in the color tag. Please do not suggest using heatmaps either as this is intended to be a development work.
Here is the code:
<!DOCTYPE html>
<html>
<head>
<title>GeoJSON example</title>
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.5.0/ol.css" type="text/css">
<script src="ol3/ol.js" type="text/javascript"></script>
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
</div>
</div>
</div>
<script>
var styles = {
'Circle': [new ol.style.Style({
fill: new ol.style.Fill({
color: 'RGBA(255,0,0,0.3)'
})
})]
};
var styleFunction = function(feature, resolution) {
return styles[feature.getGeometry().getType()];
};
var geojsonObject = {
'type': 'FeatureCollection',
'crs': {
'type': 'name',
'properties': {
'name': 'EPSG:3857'
}
},
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [10, 10]
}
}, ]
};
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(geojsonObject)
});
vectorSource.addFeature(new ol.Feature(new ol.geom.Circle([10e5, 10e5], 15e5)));
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: styleFunction
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.TileWMS({
url: 'http://maps.opengeo.org/geowebcache/service/wms',
params: {
LAYERS: 'bluemarble',
VERSION: '1.1.1'
}
})
}),
vectorLayer],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */
({
collapsible: false
})
}),
view: new ol.View({
center: [10, 10],
zoom: 2
})
});
</script>
</body>
</html>
It is now possible with CanvasPattern and CanvasGradient.
Ref: https://openlayers.org/en/latest/examples/canvas-gradient-pattern.html

how to add markers with OpenLayers 3

I'm trying to add makers on a OpenLayers 3 map.
The only example I have found is this one in the OpenLayers example list.
But the example uses ol.Style.Icon instead of something like OpenLayers.Marker in OpenLayers 2.
First Question
The only difference would be that you have to set the image Url but is it the only way to add a marker?
Also OpenLayers 3 doesn't seem to come with marker images so it would make sense if there's no other way than ol.Style.Icon
Second Question
It would be really nice if someone could give me an example of a function to add markers or icons after the map is loaded.
From what I understand in the example, they create a layer for the icon
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([-72.0704, 46.678], 'EPSG:4326', 'EPSG:3857')),
name: 'Null Island',
population: 4000,
rainfall: 500
});
var iconStyle = new ol.style.Style({
image: new ol.style.Icon(/** #type {olx.style.IconOptions} */ ({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
opacity: 0.75,
src: 'data/icon.png'
}))
});
iconFeature.setStyle(iconStyle);
var vectorSource = new ol.source.Vector({
features: [iconFeature]
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
Then they set the icon layer when they initialize the map
var map = new ol.Map({
layers: [new ol.layer.Tile({ source: new ol.source.OSM() }), vectorLayer],
target: document.getElementById('map'),
view: new ol.View2D({
center: [0, 0],
zoom: 3
})
});
If I want to add many markers, do I have to create 1 layer for each marker?
How could I add many markers to a layer? I can't figure out how this part would look
like
var vectorSource = new ol.source.Vector({
features: [iconFeature]
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
Thank you
Q1. Markers are considered deprecated in OpenLayers 2, though this isn't very obvious from the documentation. Instead, you should use an OpenLayers.Feature.Vector with an externalGraphic set to some image source in its style. This notion has been carried over to OpenLayers 3, so there is no marker class and it is done as in the example you cited.
Q2. The ol.source.Vector takes an array of features, note the line, features: [iconFeature], so you would create an array of icon features and add features to that, eg:
var iconFeatures=[];
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([-72.0704, 46.678], 'EPSG:4326',
'EPSG:3857')),
name: 'Null Island',
population: 4000,
rainfall: 500
});
var iconFeature1 = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([-73.1234, 45.678], 'EPSG:4326',
'EPSG:3857')),
name: 'Null Island Two',
population: 4001,
rainfall: 501
});
iconFeatures.push(iconFeature);
iconFeatures.push(iconFeature1);
var vectorSource = new ol.source.Vector({
features: iconFeatures //add an array of features
});
var iconStyle = new ol.style.Style({
image: new ol.style.Icon(/** #type {olx.style.IconOptions} */ ({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
opacity: 0.75,
src: 'data/icon.png'
}))
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: iconStyle
});
Obviously, this could be more elegantly handled by putting all of the ol.Feature creation inside a loop based on some data source, but I hope this demonstrates the approach. Note, also, that you can apply a style to the ol.layer.Vector so that it will be applied to all features being added to the layer, rather than having to set the style on individual features, assuming you want them to be the same, obviously.
EDIT: That answer doesn't seem to work. Here is an updated fiddle that works by adding the features (icons) to the empty vector source in a loop, using vectorSource.addFeature and then adds the whole lot to the layer vector afterwards. I will wait and see if this works for you, before updating my original answer.
there's a good tutorial at: http://openlayersbook.github.io
not tested but may helpful
var features = [];
//iterate through array...
for( var i = 0 ; i < data.length ; i++){
var item = data[i]; //get item
var type = item.type; //get type
var longitude = item.longitude; //coordinates
var latitude = item.latitude;
/*....
* now get your specific icon...('..../ic_customMarker.png')
* by e.g. switch case...
*/
var iconPath = getIconPath(type);
//create Feature... with coordinates
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([longitude, latitude], 'EPSG:4326',
'EPSG:3857'))
});
//create style for your feature...
var iconStyle = new ol.style.Style({
image: new ol.style.Icon(/** #type {olx.style.IconOptions} */ ({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
opacity: 0.75,
src: iconPath
}))
});
iconFeature.setStyle(iconStyle);
features.push(iconFeature);
//next item...
}
/*
* create vector source
* you could set the style for all features in your vectoreSource as well
*/
var vectorSource = new ol.source.Vector({
features: features //add an array of features
//,style: iconStyle //to set the style for all your features...
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
map.addLayer(vectorLayer);
var exampleLoc = ol.proj.transform(
[131.044922, -25.363882], 'EPSG:4326', 'EPSG:3857');
var map = new ol.Map({
target: 'map',
renderer: 'canvas',
view: new ol.View2D({
projection: 'EPSG:3857',
zoom: 3,
center: exampleLoc
}),
layers: [
new ol.layer.Tile({source: new ol.source.MapQuest({layer: 'osm'})})
]
});
map.addOverlay(new ol.Overlay({
position: exampleLoc,
element: $('<img src="resources/img/marker-blue.png">')
.css({marginTop: '-200%', marginLeft: '-50%', cursor: 'pointer'})
.tooltip({title: 'Hello, world!', trigger: 'click'})
}));
map.on('postcompose', function(evt) {
evt.vectorContext.setFillStrokeStyle(
new ol.style.Fill({color: 'rgba(255, 0, 0, .1)'}),
new ol.style.Stroke({color: 'rgba(255, 0, 0, .8)', width: 3}));
evt.vectorContext.drawCircleGeometry(
new ol.geom.Circle(exampleLoc, 400000));
});
var exampleKml = new ol.layer.Vector({
source: new ol.source.KML({
projection: 'EPSG:3857',
url: 'data/example.kml'
})
});
map.addLayer(exampleKml);
We just finished updating our website NUFOSMATIC from ol2 to ol6. Both the ol2 and ol3 code is on the site. This includes Matt Walker's ol-layerswitcher https://github.com/walkermatt replacing the missing ol2 layerswitcher. We actually updated three map applications; HEATMAP replaces the Patrick Wied (http://www.patrick-wied.at) ol2 heatmap with the native ol6 heatmap.
Only took 6 days. Wonder why we waited this long... oh, yeah, we have day jobs...

Categories

Resources