OpenLayers, nice marker clustering - javascript

Do you know how to have a nice clustering in OpenLayers such as this google example ?

You can add label to pointStyle in above example and explain context of this label.
Your code should be something like this:
var pointStyle = new OpenLayers.Style({
// ...
'label': "${label}",
// ...
}, {
context: {
// ...
label: function(feature) {
// clustered features count or blank if feature is not a cluster
return feature.cluster ? feature.cluster.length : "";
}
// ..
}
});
var styleMap = new OpenLayers.StyleMap({
'default': pointStyle,
});
var googleLikeLayer = new OpenLayers.Layer.Vector("GoogleLikeLayer", {
// ...
styleMap : styleMap,
// ...
});

Use OpenLayers.Strategy.Cluster for clustering.
Example Code
Working Example
Custom Styling
In-depth Explanation

I have just implemented a so called AnimatedCluster strategy for OpenLayers.
You can see a bit more about it at: http://www.acuriousanimal.com/2012/08/19/animated-marker-cluster-strategy-for-openlayers.html
It is only a firts version but adds a nice animation to the clusters. There are many things to improve but it is a starting point.

There's a great clustering example available in OpenLayers 3.
I created a jsFiddle from the code so you can play with it.
Basically you have to create an ol.source.Cluster with a grouping distance from an ol.source.Vector formed by an array of ol.Feature. Each ol.Feature created from your source coordinates in the form of ol.geom.Point.
var features = [
new ol.Feature(new ol.geom.Point([lon1, lat1])),
new ol.Feature(new ol.geom.Point([lon2, lat2])),
...
];
var cluster = new ol.source.Cluster({
distance: 50,
source: new ol.source.Vector({ features: features });
});
var map = new ol.Map({
layers: [
new ol.source.MapQuest({layer: 'sat'}), // Map
new ol.layer.Vector({ source: cluster }) // Clusters
],
renderer: 'canvas',
target: 'map'
});

you can do this with as igorti has said. the soltion is using OpenLayers.Strategy.Cluster class and styling your layer with OpenLayers.Style class...
for styling :
var pointStyle = new OpenLayers.Style({
'default': new OpenLayers.Style({
'pointRadius': '${radius}',
'externalGraphic': '${getgraph}'
....
},{
context:{
radius: function(feature){
return Math.min(feature.attributes.count,7)+3;
},{
getgraph : function(feature){
return 'ol/img/googlelike.png';
}}}};
it must helps you, more power to you!

Here is the JSfiddle for clustering based on custom attributes added to the layers. I struggled a bit with this so putting it here; Also shows creating a summary pie graph image when zoomed out with the clustered data http://jsfiddle.net/alexcpn/518p59k4/
Also created a small openlayer tutorial to explain this OpenLayers Advanced Clustering
var getClusterCount = function (feature) {
var clustercount = {};
var planningcount = 0;
var onaircount = 0;
var inerrorcount = 0;
for (var i = 0; i < feature.cluster.length; i++) {
if (feature.cluster[i].attributes.cluster) {
//THE MOST IMPORTANT LINE IS THE ONE BELOW, While clustering open layers removes the orginial feature layer with its own. So to get the attributes of the feature you have added add it to the openlayer created feature layer
feature.attributes.cluster = feature.cluster[i].attributes.cluster;
switch (feature.cluster[i].attributes.cluster) {
......
return clustercount;
};

Related

Leaflet clickable grid in polygon

I'm using leaflet.js to show reforestation efforts.
Is there any possibility to create a grid-pattern where every square of the pattern can be linked to a click-event?
Something similar like this.
It would need small squares that would together form a similar polygon as shown above.
I tried Leaflet-pattern, but the squares resize on zoom and there is no option to add an event to the pattern shapes.
Could I use the leaflet rectangle for this? How would I find the correct latitude and longitudes for each square?
You probably want to create a square grid, then calculate the intersection between each grid cell with each polygon.
It's up to you to decide the details of the square grid, and whether you want the same grid for all polygons, or a different grid for each polygon.
Thanks to Ivan Sanchez I found the solution.
For anyone looking for it see this JSFiddle.
'''
// initialize the map on the "map" div with a given center and zoom
var map = L.map(
'map', {
layers: [map]
}
).setView([-5.0, 19.40], 11);
//HexGrid
var bbox = [19.35, -5, 19.5, -5.15];
var cellSide = 1;
var options = {
units: 'kilometers'
};
var hexgrid = turf.hexGrid(bbox, cellSide, options);
//Polygram that will contain the hexagons
let poly1 = turf.polygon([
[
[19.4, -5],
[19.5, -5.1],
[19.4, -5.1],
[19.4, -5]
]
]);
_.each(hexgrid.features, function(hex) {
var intersection = turf.intersect(poly1, hex.geometry);
if (intersection) {
hex.geometry = intersection.geometry;
} else {
hex.geometry = {
type: "Polygon",
coordinates: []
}
}
})
L.geoJSON(hexgrid, {
style: function(feature) {
return {
weight: 1
};
}
}).addTo(map);
L.geoJSON(poly1).addTo(map);
'''

Openlayers 4: loading features from array

So, i'm pretty new to JS.
I'm creating features styled as markers in the center of my rgb layers and assign them to an array with this code:
for (var i = 0, len = Layers.length; i < len; i++) {
var mExtent = ol.proj.transformExtent(Layers[i].BoundingBox[0].extent, 'EPSG:4326', 'EPSG:3857');
var X = mExtent[0] + (mExtent[2]-mExtent[0])/2;
var Y = mExtent[1] + (mExtent[3]-mExtent[1])/2;
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point([X, Y]),
name: Layers[i].Title,
layername: Layers[i].Name,
description: Layers[i].Abstract
});
var iconStyle = new ol.style.Style({
image: new ol.style.Icon(/** #type {olx.style.IconOptions} */ ({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: ortho
}))
});
iconFeature.setStyle(iconStyle);
var vectorSource = new ol.source.Vector({
features: [iconFeature]
});
var vectorLayer = new ol.layer.Vector({source: vectorSource, zIndex: 100 });
layers2[i] = vectorLayer
}
when im trying then to call a map with:
var map = new ol.Map({
layers:[BaseLayers, POI, layers2],
target: document.getElementById('map')
});
My layers2 array of features does not show up on the map.
If then i try to add this array of features manually in the console:
map.addLayer(layers2)
I get following error:
TypeError: a.addEventListener is not a function
But if i try to manually call an element from array like such:
map.addLayer(layers2[0])
It works fine.
My array containing base layers(OSM+mapbox) works fine.
Im pretty sure there's something wrong with my type of array.
But don't know what.
Thanks for coming by.
Edit 1
Tried to put all my features, rgb layers and basemaps in a single array "layers".
So the code changed in a first loop from
layers2[i] = vectorLayer;
To:
layers.push(vectorLayer);
Where "layers" already contains all the rest layer objects.
When calling the map - no "vectorLayer" features are on it.
When calling "layers" manually in console with map.addLayer(layers) still get the same error.
When calling specific "vectorLayer" feature with map.addLayer(layers[2]), for example - it show's up.
By looking at the code layers2 is an array of layers. While creating ol.Map object layers attribute should be single dimension array but in your code its a 2 dimensional array like below. Thats the reason its giving error.
[ BaseLayer, POI, [layers[0],layers2[1],.....]]
Solution 1: Add array of layers after creating the map object using loop.
var map = new ol.Map({
layers:[BaseLayers, POI],
target: document.getElementById('map')
});
for ( var i=0; i<layers2.length; i++ ) {
map.addLayer(layers2[i]);
};
Solution 2 : Insert BaseLayers and POI to layers2 then configure ol.Map by declaring only layers2.

Assign multiple feature group in edit control in leaflet draw

How can i assign multiple the feature groups in edit control,
Assume we have two feature groups(It can be multiple),and we want to assign edit control to both groups.
This is groups,
var attLayerGroup = new L.FeatureGroup();
var photoLayerGroup = new L.FeatureGroup();
And this is, How i assign control to groups,
var drawControl = map.addControl(new L.Control.Draw({
edit: {
featureGroup: photoLayerGroup,attLayerGroup,
poly: {
allowIntersection: true
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
}
}
}));
Is it right way to assigning Edit control to FeatureGroup ?
If not,How can we do it?
If there is no specific distinction between your 2 initial Feature Groups, then simply copy the reference of each individual layer into a new parent Feature Group and use the latter as Leaflet.draw featureGroup option:
var fg = L.featureGroup();
photoLayerGroup.eachLayer(function (layer) {
if (!layer instanceof L.LayerGroup) {
fg.addLayer(layer);
}
});
// Same for attLayerGroup.
new L.Control.Draw({
edit: {
featureGroup: fg
}
});
Leaflet.Draw featureGroup does not accept multiple feature groups.
You can have two instances of Leaflet.Draw set up, one for each feature group. However, I predict you will have issues with the buttons Leaflet.Draw sets up and possibly the events thrown by each instance.
The second option is to have a single instances of Leaflet.Draw and you swap the feature group with a toggle.

Openlayers-3 Raster Layer not Changing when Sources Changed

My server is statically serving several different PNG images of the same object, each taken with a different spectral filter (for example, just a red channel or just a blue channel). I'd like to show a slippy, false-colored map of that object. I do so by creating three separate images sources like so:
extent = [0, 0, ncols, nrows];
pixelProjection = new ol.proj.Projection({
code: 'some-image',
units: 'pixels',
extent: extent
});
rsource = new ol.source.ImageStatic({
url: "static/imgs/band_1.png",
projection: pixelProjection,
imageExtent: extent
});
gsource = new ol.source.ImageStatic({
url: "static/imgs/band_2.png",
projection: pixelProjection,
imageExtent: extent
});
bsource = new ol.source.ImageStatic({
url: "static/imgs/band_3.png",
projection: pixelProjection,
imageExtent: extent
});
Next, I use these sources as inputs to a raster source which can compose them:
rgbSources = [rsource, gsource, bsource];
raster = new ol.source.Raster({
sources: rgbSources,
operation: function(bands, data) {
var rband = bands[0];
var gband = bands[1];
var bband = bands[2];
var composed = [
rband[0],
gband[0],
bband[0],
255
];
return composed;
}
});
I then create a layer that uses this raster as its source:
colorLayer = new ol.layer.Image({
source: raster
});
Lastly, I can create a map and add my raster layer to the map:
var map = new ol.Map({
target: 'map',
view: new ol.View({
center:ol.extent.getCenter(extent),
projection: pixelProjection,
zoom: 1.5
})
});
map.addLayer(colorLayer);
So far so good! This displays a colorized version of the image as expected. The problem arises when the user triggers a change to a color channel by inputting a new channel index to pull from. I handle a blue channel change like this:
var index = 4; // actually gets passed in from user
bsource = new ol.source.ImageStatic({
url: "static/imgs/band_" + index + ".png",
projection: pixelProjection,
imageExtent: extent
});
rgbSources[2] = bsource; // this was in global scope from before
raster.sources = rgbSources; // as was this
Expected behavior is that the map would immediately change colors, or at least it would change when I zoom in or pan but neither of those things happens. I am unable to get the new colors to appear at all. Am I updating the wrong thing? Perhaps the raster.sources field has an associated setter function that I am unable to find?
Found a solution! It looks like setting a raster's source directly is not allowed, but setting a layer's source is. So unfortunately, I have to create a new raster object (new source entirely), but at least I don't need a new layer:
raster = new ol.source.Raster({
sources: rgbSources,
operation: composeBands
});
colorLayer.setSource(raster);
Accepting my own answer but willing to accept someone else's solution if it means I don't need to create a new source.

Passing Array to Openlayers 3 with geolocation and style

I am looking to create simple markers in Openlayers 3 based on a two dimensional array which I pass via PHP containing the Geolocation of the marker and an attribute. Currently the attribute is the colour that I would like the layer to be:
//run external php script to grab all entries from the database; make an array and add to source vector
var latlong_marker = <?php require 'db_multiple_gps.php'; echo $ol_latlong;?>;
var latlong_array_length = latlong_marker.length;
var vectorSource = new ol.source.Vector({
//create empty vector
});
// cycle through all entries in the array
for (var i = 0; i < latlong_array_length; i++){
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(latlong_marker[i][0], 'EPSG:4326', 'EPSG:3857'))});
vectorSource.addFeature(iconFeature);
var iconStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({color: latlong_marker[i][2]})
})
});
//add the feature vector to the layer vector, and apply a style to whole layer
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: iconStyle
});
}
The vector layers are presented on the map in the correct locations but the issue is that the colour is that of the last entry in the array latlong_marker[i][2].
From my research I think it may be due to closure of functions but I'm not exactly sure how or where the functions are in play here.
Debugging show the entire array being passed through the loop and vectorSource looking good; however iconStyle appears to have many null values.
Am I going about this in the right fashion? I could pass another parameter other than the actual colour required (such as a integer) to then be used to create the different style.
In addition to the code snippet above, I have played around with with closing the loop earlier; this works fro creation of vectorSource but I always fall down with trying to pass an array of iconStyle to the Vector Layer.
Try this
var latlong_marker = <?php require 'db_multiple_gps.php'; echo $ol_latlong;?>;
var latlong_array_length = latlong_marker.length;
var vectorSource = new ol.source.Vector({
//create empty vector
});
// cycle through all entries in the array
for (var i = 0; i < latlong_array_length; i++){
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(latlong_marker[i][0],
'EPSG:4326','EPSG:3857'))});
var iconStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({color: latlong_marker[i][2]})
})
});
// THIS IS NEW - add each style individually to the feature
iconFeature.setStyle(iconStyle);
// First add the feature when it has got its style
vectorSource.addFeature(iconFeature);
}
//add the feature vector to the layer vector, and apply a style to whole layer
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
// remove this - > style: iconStyle
});

Categories