OpenLayers 2.13: clustering strategy is not working - javascript

I am trying to apply a simple clustering stategy to my OpenLayers v2.13 map, but it is not working.
Here is my code so far, it all loads correctly but the random points on the map do not cluster, they just overlap horribly...
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<title>OpenLayers 2.13.x Clustered Markers</title>
<script src="../OpenLayers.js"></script>
</head>
<body onload="run()" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div id='map' style="width: 100%; height: 100%">
</div>
<script>
function run(){
// create the map
var map = new OpenLayers.Map("map");
// add a google maps layer to the map
var layer = new OpenLayers.Layer.WMS("OpenLayers WMS", "http://vmap0.tiles.osgeo.org/wms/vmap0", {
layers: "basic"
});
map.addLayers([layer]);
// set up cluster strategy and vector layer
var strategy = new OpenLayers.Strategy.Cluster({
distance: 15,
clustering: true
});
var markersLayer = new OpenLayers.Layer.Vector("Clustered markers", {strategies: [strategy]});
// create and add all markers randomly
var markers = [];
for (var i = 0; i < 700; i++) {
var r1 = Math.random();
var r2 = Math.random();
var r3 = Math.random();
var r4 = Math.random();
var px = r1 * 180 * ((r2 < 0.5) ? -1 : 1);
var py = r3 * 90 * ((r4 < 0.5) ? -1 : 1);
var p = new OpenLayers.Geometry.Point(px, py);
var clazz = (i % 10 === 0) ? 4 : Math.ceil(r4 * 3);
var f = new OpenLayers.Feature.Vector(p, {clazz: clazz});
markers.push(f);
}
markersLayer.addFeatures(markers);
// add markers layer to the map
map.addLayer(markersLayer);
map.zoomToMaxExtent();
}
</script>
</body>
</html>
Note: OpenLayers is locally on my machine and is version 2.13.1
I have looked at several examples, none have helped me solve this issue. I have looked at several stack overflow answers, the best of them was about marker clustering, but also didn't help.
I must be missing something obvious but I cant see what?
[UPDATE]
Taking advice from the answers below, here is the code snippet (from above) edited to run correctly, adding the markers after the layer has been added to the map and not including the clustering flag...
// set up cluster strategy and vector layer
var strategy = new OpenLayers.Strategy.Cluster({
distance: 15 // <-- removed clustering flag
});
var markersLayer = new OpenLayers.Layer.Vector("Clustered markers", {strategies: [strategy]});
// add markers layer to the map
map.addLayer(markersLayer); // <-- adding layer before adding features
// create and add all markers randomly
var markers = [];
for (var i = 0; i < 700; i++) {
var r1 = Math.random();
var r2 = Math.random();
var r3 = Math.random();
var r4 = Math.random();
var px = r1 * 180 * ((r2 < 0.5) ? -1 : 1);
var py = r3 * 90 * ((r4 < 0.5) ? -1 : 1);
var p = new OpenLayers.Geometry.Point(px, py);
var clazz = (i % 10 === 0) ? 4 : Math.ceil(r4 * 3);
var f = new OpenLayers.Feature.Vector(p, {clazz: clazz});
markers.push(f);
}
markersLayer.addFeatures(markers); // <-- now can add features
// zoom to extent
map.zoomToMaxExtent();
It looks like maybe a good practice to follow is to make sure that you add a layer to the map before adding/removing features to it.

I removed "clustering" from the cluster strategy options
// set up cluster strategy and vector layer
var strategy = new OpenLayers.Strategy.Cluster({
distance: 15
});
and then added the markers after I'd added the layer to the map
// add markers layer to the map
map.addLayer(markersLayer);
markersLayer.addFeatures(markers);
map.zoomToMaxExtent();
then all seemed to work.

Mailed similar to Angela to you internally.
Not sure why removing clustering has any affect, I think its true by default anyway.
As for the order of adding the points, I seem to remember reading something about the fact your points are replaced by the clusters so adding the layer to map after adding points to layer may mean that process doesn't happen. Or something. ;)
Cheers
Ian

Related

How to get MapMarker inside a polygon with clustering active

We're looking for a way to get our data points that are inside a polygon using JavaScript HereMaps API
We're adding 4 datapoints to a ClusterLayer / ClusterProvider and a polygon to the map. 3 of the 4 points are within the drawn Polygon (data of the points: a, b, d). Point with data = c is not within the polygon (see jsfiddle)
We tried to use map.getObjectsWithin but this functions only returns the polygon. We assume that this is caused by the different layers.
What's the best way to get the datapoints which are in the bounds of the polygon?
We try to avoid additional dependencies to solve this issue.
quick demo:
http://jsfiddle.net/4dno0gu2/59/
We found this question, but there wasn't any example, no activity for a long time and no solution.
HereMap getObjectsWithin does not show objects in LocalObjectProvider
Yeah the method getObjectsWithin doesn't work for ObjectProvider although this implemented in JS API but simple this functionality is not a public method, apologies for it.
Short description of workaround:
obtain the bounding box of the polygon
request all types of objects where you interested in for this bounding box from the provider
(requestOverlays, requestSpatials, requestMarkers etc.) - in your case requestMarkers
filter out all objects which doesn't intersect with the polygon
Code:
/**
* Adds a polygon to the map
*
* #param {H.Map} map A HERE Map instance within the application
*/
function addPolygonToMap(map) {
var geoStrip = new H.geo.LineString(
[48.8, 13.5, 100, 48.4, 13.0, 100, 48.4, 13.5, 100]
);
var polygon = new H.map.Polygon(geoStrip, {
style: {
strokeColor: '#829',
lineWidth: 8
},
data: 'polygon'
});
//map.addObject(polygon);
return polygon;
}
function logObjectsInPolygon(map, polygon){
var geoPolygon = polygon.getGeometry();
map.getObjectsWithin(geoPolygon, (o) => {
console.log('found mapObjects: '+ o.length);
o.forEach(x=>{
if(typeof x.getData === 'function'){
console.log(x.getData());
}
});
});
}
function isPointInPolygon(testPoint, polygPoints) {
let result = false;
let j = polygPoints.length - 1;
for(i=0,len=j+1; i<len; i++){
let p = polygPoints[i];
let lP = polygPoints[j];
if(p.y < testPoint.y && lP.y >= testPoint.y || lP.y < testPoint.y && p.y >= testPoint.y){
if((p.x + (testPoint.y - p.y) / (lP.y - p.y) * (lP.x - p.x)) < testPoint.x){
result = !result;
}
}
j = i;
}
return result;
}
/**
* Boilerplate map initialization code starts below:
*/
//Step 1: initialize communication with the platform
var platform = new H.service.Platform({
apikey: 'H6XyiCT0w1t9GgTjqhRXxDMrVj9h78ya3NuxlwM7XUs',
useCIT: true,
useHTTPS: true
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map - this map is centered over Europe
var map = new H.Map(document.getElementById('map'),
defaultLayers.raster.normal.map,{
center: {lat:48.5, lng:13.45},
zoom: 10
});
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
//this marker should go to clusters if there is more data points
var dataPoints = [];
dataPoints.push(new H.clustering.DataPoint(48.5, 13.45,{}, 'a'));
dataPoints.push(new H.clustering.DataPoint(48.5001, 13.45,{}, 'b'));
dataPoints.push(new H.clustering.DataPoint(48.5002, 13.51,{}, 'c')); // not in Polygon
dataPoints.push(new H.clustering.DataPoint(48.53, 13.45,{}, 'd'));
var clusteredDataProvider = new H.clustering.Provider(dataPoints);
var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
map.addLayer(layer);
// createPolygon to select clustered (noise) points
var polygon = addPolygonToMap(map);
let extPoly = polygon.getGeometry().getExterior();
let seqPointsPoly = [];
extPoly.eachLatLngAlt((lat, lng, alt, idy) => {
seqPointsPoly.push( {y: lat, x: lng});
});
console.log("seqPointsPoly:", seqPointsPoly);
map.addEventListener("tap", (e) => {
let pBbox = polygon.getBoundingBox();
let arrPnts = clusteredDataProvider.requestMarkers(pBbox);
for(let i=0,len=arrPnts.length; i<len; i++){
let m = arrPnts[i];
let p = {y: m.getGeometry().lat, x: m.getGeometry().lng};
let clustData = m.getData();
if(!clustData.getData){
console.log("cluster: is in polygon:", isPointInPolygon(p, seqPointsPoly));
} else if(clustData.getData){
console.log("nois: is in polygon:", clustData.getData(), m.getGeometry(), isPointInPolygon(p, seqPointsPoly));
}else{
console.log("unknown type");
}
}
console.log("clusteredDataProvider:", pBbox, clusteredDataProvider.requestMarkers(pBbox));
// Our expected logging is: points a, b, d
//logObjectsInPolygon(map, polygon);
});
Worked example (tap on map to start processing): http://jsfiddle.net/m1ey7p2h/1/

OpenLayers creating a complex style (polygon with a hole and a stroke on one side)

There was a difficulty in creating a complex polygon style.
The wording is as follows:
the polygon should be drawn as a polygon with a hole and a stroke on the outside.
In a difficult (as it seems to me) way, I made drawing a polygon with a hole:
convert to turf
using turf.buffer and a negative buffer value, I get an internal buffer
using turf.difference (source polygon and buffer) I get a polygon with a hole
But I don't understand how to draw the border only from the outside%)
If in the same function I try to return 2 styles (line + polygon), then I get an error (Uncaught TypeError: s.simplifyTransformed is not a function).
In general, is it possible to return 2 different geometries in the style?
In the picture the red polygon is what I need to get in the end.
Also I made a minimal example on codepen
I would be grateful for your help!
upd.
loops
and zoom out
To adapt the OpenLayers 3: Offset stroke style example for a polygon you would need to extend the ring by one segment at each end so you can correctly calculate the new coordinates at the original start/end point, then remove the excess when creating the resulting polygon.
var style = function(feature, resolution) {
var poly = feature.getGeometry();
if (poly.getType() == 'Polygon') {
var coordinates = poly.getCoordinates()[0];
coordinates = coordinates.slice(-2, -1).concat(coordinates).concat(coordinates.slice(1, 2));
var geom = new ol.geom.LineString(coordinates);
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 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.Polygon([coords.slice(2, -1)]),
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: 'Polygon',
style: style
}));
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>
There is a problem with the original algorithm for LineStrings at corners with multiple vertices
When zoomed out the two vertices on the inner line should merge to a single point, but that is not happening, instead they cross and cause a kink in the line.

I want add these circles onClick of mouse on Openlayer map

I want to make a circle on map using jQuery. In this given code circles are made randomly. But I want to make only one circle on click. Openlayer.js can be found on Openlayer website.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<script type='text/javascript' src='OpenLayers.js'></script>
<script type='text/javascript'>
var map;
var vector_layer;
function init() {
//Create a map with an empty array of controls
map = new OpenLayers.Map('map_element');
//Create a base layer
var wms_layer = new OpenLayers.Layer.WMS(
'OpenLayers WMS',
'http://vmap0.tiles.osgeo.org/wms/vmap0',
{layers: 'basic'},
{}
);
map.addLayer(wms_layer);
//Add vector layer
vector_layer = new OpenLayers.Layer.Vector('Settlement Vector Layer');
map.addLayer(vector_layer);
var settlement_values = {
4: 'circle'
}
//Create some points
for(var i=0; i<20; i++){
vector_layer.addFeatures([new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Point(
(Math.floor(Math.random() * 360) - 180),
(Math.floor(Math.random() * 180) - 90)
),
{
'settlement_type': settlement_values[(Math.floor(Math.random() * 5))]
}
)]);
}
//Create a style map object
var vector_style_map = new OpenLayers.StyleMap({});
//ADD RULES
//We need to create a 'lookup table' that contains the desired values
// and corresponding symbolizer
var symbolizers_lookup = {
'circle': {
'fillColor': '#336699','fillOpacity':.8, 'pointRadius':50, 'strokeColor': '#003366', 'strokeWidth':2
}
}
//Now, call addUniqueValueRules and pass in the symbolizer lookups
vector_style_map.addUniqueValueRules('default', 'settlement_type', symbolizers_lookup);
//Add the style map to the vector layer
vector_layer.styleMap = vector_style_map;
if(!map.getCenter()){
map.zoomToMaxExtent();
}
}
</script>
</head>
<body onload='init();'>
<div id='map_element' style='width: 600px; height: 600px;'></div>
</body>
</html>
I strongly recommend you to study this: http://openlayers.org/en/latest/examples/draw-features.html
Here is a minimalist edition of the example:
var draw; // global so we can remove it later
function addInteraction() {
var value = "circle"
draw = new ol.interaction.Draw({
source: source,
type: /** #type {ol.geom.GeometryType} */ (typeSelect.value)
});
map.addInteraction(draw);
}
addInteraction();
Although it does not create shapes by jquery, it allows to draw circles on the map.
Hope it helps, happy coding :)
Yes If you are talking in Openlayer2 the this will help you cheers.further I have attached code in jsfiddle
`
var point1 = new OpenLayers.Geometry.Point(0, 0);
var point2 = new OpenLayers.Geometry.Point(5000000, 1000000);
var point3 = new OpenLayers.Geometry.Point(2000000, 2000000);
var radius = $("#amount").val();
var mycircle = OpenLayers.Geometry.Polygon.createRegularPolygon(point2, radius, 20, 0);
var featurecircle = new OpenLayers.Feature.Vector(mycircle);
marker1 = new OpenLayers.Feature.Vector(point1, null, {
externalGraphic: "marker.png",
graphicWidth: 32,
graphicHeight: 32,
fillOpacity: 1
});
marker1.style = {
display: 'none'
};
http://jsfiddle.net/zLjae81b/18/`

Tiling contiguous polygons in Google Maps

I'm trying to draw a hexagonal grid in Google Maps. I've come up with a solution based off this answer which looks fine at higher zooms, but when zoomed further out I find that the classic "orange-peel" problem occurs: The hexagons no longer fit together like they should:
I'm using this rather cool geodesy library to calculate hexagon centers based on an ellipsoidal model (since a 2d model clearly doesn't work on a real-world map) but it's still looking pretty bad when zoomed out.
Preferably, I'd like to draw the hexagons in such a way that they are exactly the same shape and size on screen.
Here's the code I've been working with, also available as a Plunker here. I've tried calculating the vertices of each polygon using the same geodesy library that I'm using to calculate the polygon centers, but it still doesn't look right when zoomed out.
var hexgrid = [];
function initialize(){
// Create the map.
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 51.5, lng: 0},
scrollwheel: true,
zoom: 8
});
// This listener waits until the map is done zooming or panning,
// Then clears all existing polygons and re-draws them.
map.addListener('idle', function() {
// Figure out how big our grid needs to be
var spherical = google.maps.geometry.spherical,
bounds = map.getBounds(),
cor1 = bounds.getNorthEast(),
cor2 = bounds.getSouthWest(),
cor3 = new google.maps.LatLng(cor2.lat(), cor1.lng()),
cor4 = new google.maps.LatLng(cor1.lat(), cor2.lng()),
diagonal = spherical.computeDistanceBetween(cor1,cor2),
gridSize = diagonal / 20;
// Determine the actual distance between tiles
var d = 2 * gridSize * Math.cos(Math.PI / 6);
// Clear all the old tiles
hexgrid.forEach(function(hexagon){
hexagon.setMap(null);
});
hexgrid = [];
// Determine where the upper left-hand corner is.
bounds = map.getBounds();
ne = bounds.getNorthEast();
sw = bounds.getSouthWest();
var point = new LatLon(ne.lat(), sw.lng());
// ... Until we're at the bottom of the screen...
while(point.lat > sw.lat()){
// Keep this so that we know where to return to when we're done moving across to the right
leftPoint = new LatLon(point.lat, point.lon).destinationPoint(d, 150).destinationPoint(d, 210).destinationPoint(d, 270).destinationPoint(d, 90)
step = 1;
while(point.lon < ne.lng()){
// Use the modulus of step to determing if we want to angle up or down
if (step % 2 === 0){
point = new LatLon(point.lat, point.lon).destinationPoint(d, 30);
} else {
point = new LatLon(point.lat, point.lon).destinationPoint(d, 150);
}
step++; // Increment the step
// Draw the hexagon!
// First, come up with the corners.
vertices = [];
for(v = 1; v < 7; v++){
angle = v * 60;
vertex = point.destinationPoint(d / Math.sqrt(3), angle);
vertices.push({lat: vertex.lat, lng: vertex.lon});
}
// Create the shape
hexagon = new google.maps.Polygon({
map: map,
paths: vertices,
strokeColor: '#090',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#090',
fillOpacity: 0.1,
draggable: false,
});
// Push it to hexgrid so we can delete it later
hexgrid.push(hexagon)
}
// Return to the left.
point = leftPoint;
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Please consider that Google Maps is in Mercator Projection.
You have to compensate for the sphere of the globe on the projection.
https://en.wikipedia.org/wiki/Mercator_projection

How to add text on circle and how to combine circle when zoom out different cicle

I have more than 1000 markers at 3 different location in my Map.
I read Lat and Long from a text file and render it in Google maps and create circle over them on the basis of country name. And count the number of markers in those circle.
Now what i am not able to do is?
(1) I have to print the text of count on those circle (How to do without using Cluster ?)
(2) When i zoom out circle overlaps . So whenever circle overlaps it should combine the 2 radius of these two circles and should make one big circle covering the markers of the two smaller (smaller 2 will now disappear resulting on total of marker on the bigger one only).
My full code to do this (http://prntscr.com/6kt30w) is :
$.ajax({
type: 'GET',
url: './App_Start/TextFile/latLongList3.txt',
dataType: 'text',
}).success(function (data)
{
var s2 = data.replace(/^.*$/, " ").replace(/\r\n/g, " ");
var array = s2.split(/[ ]+/g);
var test = [].concat.apply([], array.map(function (array) { return array.split(/\s+/); }))
var col1 = [];
var col2 = [];
var col3 = [];
var j = 0;
for (var i = 0; i <= test.length - 3; i = i + 3)
{
col1[j] = test[i];
col2[j] = test[i + 1];
col3[j] = test[i + 2];
var myLatlng = new google.maps.LatLng(col3[j], col2[j]);
marker = new google.maps.Marker(
{
position: myLatlng,
map: map,
title: 'Hello World! ' + col1[j]
});
markers.push(marker);
if (j > 0) {
LatLong[j] = myLatlng;
}
j++;
}
})
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
I don't want to use MarkerClusterer because it is very small. Yes it's true that it will do the 2 tasks i want to accomplish but the problem i feel with it is when i have markers all over the country then MarkerClusterer just hovers over a very little part of the country whereas i want the circle to hover over all country wherever i have marker and it should have text on it. So if there is any alternative to make the size of cluster such that it cover all the markers in the entire country(because my 1 country is full of markers) and has text on it than any way to do this is welcomed too.
Could some one please help me in solving the 2 problems ?
I have to print the text of count on those circle (How to do without
using Cluster ?)
You can use the following to get count of marker from marker.length
var markers = [];
for (var i = 0; i < 100; i++) {
var latLng = new google.maps.LatLng(data.photos[i].latitude,
data.photos[i].longitude);
var marker = new google.maps.Marker({'position': latLng});
markers.push(marker);
}
To add the count to circle, you can look at this JSFiddel.
I still have to look more your second problem
Finally i found the solution of it.I simply used MarkerClusterer and got it done by increasing its size.
And it works perfectly for me.

Categories