How to draw a polygon around a polyline in JavaScript? - javascript
I want to draw a polygon around a polyline. The polyline in my case is a Google Maps direction and I need to show a polygon around it within the Google Maps canvas.
First:
For offsetting I use the JavaScript Clipper Library. I have the following polyline (route): I make an offset polygon below using Clipper:
I have a working JS Bin example.
The code is:
<html>
<head>
<title>Javascript Clipper Library / Offset polyline</title>
<script src="clipper.js"></script>
<script>
function draw() {
var polygons = [[{"X":72,"Y":59.45},{"X":136,"Y":66},{"X":170,"Y":99},{"X":171,"Y":114},{"X":183,"Y":125},{"X":218,"Y":144},{"X":218,"Y":165},{"X":226,"Y":193},{"X":254,"Y":195},{"X":283,"Y":195},{"X":292,"Y":202},{"X":325,"Y":213},{"X":341,"Y":234},{"X":397,"Y":245},{"X":417,"Y":248}]];
var scale = 100;
reverse_copy(polygons);
polygons = scaleup(polygons, scale);
var cpr = new ClipperLib.Clipper();
var delta = 25;
var joinType = ClipperLib.JoinType.jtRound;
var miterLimit = 2;
var AutoFix = true;
var svg, offsetted_polygon,
cont = document.getElementById('svgcontainer');
offsetted_polygon = cpr.OffsetPolygons(polygons, delta * scale, joinType, miterLimit, AutoFix);
//console.log(JSON.stringify(offsetted_polygon));
// Draw red offset polygon
svg = '<svg style="margin-top:10px;margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="540" height="340">';
svg += '<path stroke="red" fill="red" stroke-width="2" stroke-opacity="0.6" fill-opacity="0.2" d="' + polys2path(offsetted_polygon, scale) + '"/>';
//Draw blue polyline
svg += '<path stroke="blue" stroke-width="3" d="' + polys2path(polygons, scale) + '"/>';
svg += '</svg>';
cont.innerHTML += svg;
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale)
scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path (poly, scale) {
var path = "", i, j;
if (!scale)
scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
if (!j)
path += "M";
else
path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
function reverse_copy(poly) {
// Make reverse copy of polygons = convert polyline to a 'flat' polygon ...
var k, klen = poly.length, len, j;
for (k = 0; k < klen; k++) {
len = poly[k].length;
poly[k].length = len * 2 - 2;
for (j = 1; j <= len - 2; j++) {
poly[k][len - 1 + j] = {
X: poly[k][len - 1 - j].X,
Y: poly[k][len - 1 - j].Y
}
}
}
}
</script>
</head>
<body onload="draw()">
<h2>Javascript Clipper Library / Offset polyline</h2>
This page shows an example of offsetting polyline and drawing it using SVG.
<div id="svgcontainer"></div>
</body>
</html>
And all this is good but now I must replace the polygon variables with points from Google Maps directions, so I do this change:
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
function draw() {
var polygons = response.routes[0].overview_path;
//REST OF CODE
}
}
}
I have a JS Bin example with this code for offsetting the polygon around the polyline.
But there is some problem, which I can't regonize and I can't get a polygon around directions.
Is there any way to solve this problem?
My working solution: working example (based off of Manolis Xountasis's answer) and pieces from these related questions:
How to calculate intersection area in Google Maps API with JSTS Library?
Google Maps Polygons self intersecting detection
include the JSTS library
add routines to translate google.maps.Polyline paths to JSTS objects:
function googleMaps2JTS(boundaries) {
var coordinates = [];
var length = 0;
if (boundaries && boundaries.getLength) length = boundaries.getLength();
else if (boundaries && boundaries.length) length = boundaries.length;
for (var i = 0; i < length; i++) {
if (boundaries.getLength) coordinates.push(new jsts.geom.Coordinate(
boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
else if (boundaries.length) coordinates.push(new jsts.geom.Coordinate(
boundaries[i].lat(), boundaries[i].lng()));
}
return coordinates;
};
and back to google.maps.LatLng arrays
var jsts2googleMaps = function (geometry) {
var coordArray = geometry.getCoordinates();
GMcoords = [];
for (var i = 0; i < coordArray.length; i++) {
GMcoords.push(new google.maps.LatLng(coordArray[i].x, coordArray[i].y));
}
return GMcoords;
}
get the directions polyline from the DirectionsService and buffer it
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var overviewPath = response.routes[0].overview_path,
overviewPathGeo = [];
for (var i = 0; i < overviewPath.length; i++) {
overviewPathGeo.push(
[overviewPath[i].lng(), overviewPath[i].lat()]);
}
var distance = 10 / 111.12, // Roughly 10km
geoInput = {
type: "LineString",
coordinates: overviewPathGeo
};
var geoInput = googleMaps2JTS(overviewPath);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createLineString(geoInput);
var polygon = shell.buffer(distance);
var oLanLng = [];
var oCoordinates;
oCoordinates = polygon.shell.points[0];
for (i = 0; i < oCoordinates.length; i++) {
var oItem;
oItem = oCoordinates[i];
oLanLng.push(new google.maps.LatLng(oItem[1], oItem[0]));
}
if (routePolygon && routePolygon.setMap) routePolygon.setMap(null);
routePolygon = new google.maps.Polygon({
paths: jsts2googleMaps(polygon),
map: map
});
}
});
This is the working solution. You can find the JSTS files at coderwall.com.
var overviewPath = response.routes[0].overview_path,
overviewPathGeo = [];
for (var i = 0; i < overviewPath.length; i++) {
overviewPathGeo.push(
[overviewPath[i].lng(), overviewPath[i].lat()]
);
}
var distance = value / 10000, // Roughly 10km
geoInput = {
type: "LineString",
coordinates: overviewPathGeo
};
var geoReader = new jsts.io.GeoJSONReader(),
geoWriter = new jsts.io.GeoJSONWriter();
var geometry = geoReader.read(geoInput).buffer(distance);
var polygon = geoWriter.write(geometry);
var oLanLng = [];
var oCoordinates;
oCoordinates = polygon.coordinates[0];
for (i = 0; i < oCoordinates.length; i++) {
var oItem;
oItem = oCoordinates[i];
oLanLng.push(new google.maps.LatLng(oItem[1], oItem[0]));
}
var polygone = new google.maps.Polygon({
paths: oLanLng,
map:map
});
This is an alternate solution using Turf.js's buffer module. I have used a Leaflet map to demonstrate the results - but this will work for any mapping library.
var center = [37.78791180770003, -122.40962505340575];
var map = L.map('map').setView(center, 14);;
L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);
var line = {
"type": "Feature",
"properties": {
"color": "blue"
},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.40447521209718,
37.79367718768535
],
[-122.40803718566895,
37.79171022624846
],
[-122.40769386291502,
37.79096412372944
],
[-122.40662097930908,
37.789641468930114
],
[-122.40941047668457,
37.789675383451495
],
[-122.40992546081543,
37.78875968591083
],
[-122.40962505340575,
37.78791180770003
]
]
}
};
L.geoJSON(line, {
style: function(feature) {
return {
color: feature.properties.color
};
}
}).addTo(map);
var polygon = turf.buffer(line, 50, {
units: 'meters'
});
L.geoJSON(polygon, {
style: function(feature) {
return {
color: feature.properties.color
};
}
}).addTo(map);
#map {
height: 400px;
}
<script src="https://npmcdn.com/#turf/turf#6.3.0/turf.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js"></script>
<div id="map"></div>
Related
How to position gltf model on plane geometry tilemap in threejs where as tiles have been correctly displayed in threejs using multiple plane geometry
var c = 100; rshgroup = new THREE.Group(); facadegroup = new THREE.Group(); console.log(rshgroup); var i=0; for (var row = 0; row < 3; row++) { for (var col = 0; col < 3; col++) { var txture = new THREE.TextureLoader().load("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzA\ AABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpSoVBTuIOGSoThZERRy1CkWoEGqFVh1MXvoHTRqSFBdHwbXg4M9i1cHFWVcH\ V0EQ/AFxc3NSdJES70sKLWJ8cHkf571zuO8+QKiXmWZ1jAOabpupRFzMZFfF0CvC6EY/1YzMLGNOkpLwXV/3CPD9Lsaz/O/9uXrVnMWA\ gEg8ywzTJt4gnt60Dc77xBFWlFXic+IxkxokfuS64vEb54LLAs+MmOnUPHGEWCy0sdLGrGhqxFPEUVXTKV/IeKxy3uKslaus2Sd/YTinr\ yxznWoYCSxiCRJEKKiihDJsxGjXSbGQovO4j3/I9UvkUshVAiPHAirQILt+8D/4PVsrPznhJYXjQOeL43yMAKFdoFFznO9jx2mcAMFn4Ep\ v+St1YOaT9FpLix4BfdvAxXVLU/aAyx1g8MmQTdmVglRCPg+8n9E3ZYGBW6BnzZtb8xynD0CaZpW8AQ4OgdECZa/7vLurfW7/3mnO7wdrXHK\ kGxJ8kAAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+UMCRE4OBDcTzgAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4X\ AAAAFklEQVQI12N8cP8+AwMDEwMDAwMDAwAfkwKiW0UuOgAAAABJRU5ErkJggg=="); txture.encoding = THREE.LinearEncoding; txture.needsUpdate = true; txture.magFilter = THREE.NearestFilter; TLF=THREE.LinearFilter; var mtrial = new THREE.MeshBasicMaterial({ map: txture}); mtrial.map.minFilter = THREE.LinearFilter; mtrial.reflectivity=1; mtrial.refractionRatio=1; mtrial.opacity = 0.1; rstxt= txture; mtrial.name="mtrial_"+ row + "-" + col, rsmat= new THREE.TextureLoader(); var geometry=new THREE.PlaneGeometry(c,c); const plane = new THREE.Mesh( geometry, mtrial ); plane.needsUpdate = true; plane.position.x =(c*col)-c; plane.position.y =-6; plane.position.z =(c*row)-c; plane.rotation.x = -Math.PI /2; plane.needsUpdate = true; //create a group and add the two cubes //These cubes can now be rotated / scaled etc as a group materialstjs.push(plane); rshgroup.add( plane); i++; } } I have a seperate function to get tiles and update the material of the rshgroup with those tiles. After updating tiles material with new images i add rshgroup to scene of threejs. function PrepareTiledGround(){ lon0 = (Math.min(...facades_lon)) lat0 = (Math.min(...facades_lat)) lon1 = (Math.max(...facades_lon)) lat1 = (Math.max(...facades_lat)) var zoom = 19; var top_tile = lat2tile(lat1, zoom)-1; var left_tile = lon2tile(lon0, zoom)-1; var bottom_tile = lat2tile(lat0, zoom)+1; var right_tile = lon2tile(lon1, zoom)+1; var width = Math.abs(left_tile - right_tile) + 1; var height = Math.abs(top_tile - bottom_tile) + 1; var resolution = 156543.03 * Math.cos(lat0/180*Math.PI) / (2 ** zoom) *256 // meters/tile var lon0_tile = tile2lon(left_tile, zoom) var lat0_tile = tile2lat(bottom_tile+1, zoom) // bottom_tile+1 to get bottom (left) corner var y_offset_facade = 0.01 // Creation of Tiled Ground // Parameters var xmin = -haversineDistance([lat0,lon0],[lat0,lon0_tile]); var zmin = -haversineDistance([lat0,lon0],[lat0_tile,lon0]) ; var xmax = xmin + width*resolution; var zmax = zmin + height*resolution; var precision = { "w" : 2, "h" : 2 }; var subdivisions = { 'h' : height, 'w' : width }; if((subdivisions.h*subdivisions.w)>rshgroup.children.length){ while ((rshgroup.children.length)<(subdivisions.h*subdivisions.w)) { // code block to be executed var newModel = materialstjs[0].clone(); newModel.material=materialstjs[0].material.clone() rshgroup.add(newModel); rshgroup.updateMatrix(); rshgroup.updateWorldMatrix(true,true); } var maxrow=subdivisions.h var maxcol=subdivisions.w var i=0 var twidth=(xmax-xmin)/subdivisions.w; var theight=(zmax-zmin)/subdivisions.h; var c=256; //for (var row = subdivisions.h-1; row >=0 ; row--) { for (var row = 0; row <subdivisions.h ; row++) { for (var col = 0; col < subdivisions.w; col++) { rshgroup.children[i].material.name="mtrial_"+ row + "-" + col, rshgroup.children[i].position.x =(c*col)-c; rshgroup.children[i].position.y =0; rshgroup.children[i].position.z =(c*row)-c; i++; }} }else if((subdivisions.h*subdivisions.w)<rshgroup.children.length){ var diff=(rshgroup.children.length-(subdivisions.h*subdivisions.w)); while ((rshgroup.children.length)>(subdivisions.h*subdivisions.w)) { rshgroup.children[rshgroup.children.length-1].remove(); } } var xTileBase = left_tile // lon2tile(lon0, zoom); var yTileBase = bottom_tile //lat2tile(lat0, zoom); i=0 var c =256; for (var row = subdivisions.h-1; row >=0 ; row--) { for (var col = 0; col < subdivisions.w; col++) { const placeholder = rsmat.load("https://b.tile.openstreetmap.org/" + zoom + "/" + (xTileBase + col) + "/" + (yTileBase - row) + ".png", function ( texture ) { } ); rshgroup.children[i].material.map=placeholder; rshgroup.children[i].material.toneMapped=false; rshgroup.children[i].material.fog = false; rshgroup.children[i].material.map.needsUpdate = true; rshgroup.children[i].material.needsUpdate = true i++; } } tBox.setFromObject( rshgroup ).getCenter( rshgroup.position ).multiplyScalar( - 1 ); var object1 = scenetjs.getObjectByName( "dot" ); object1.position.set(0,0,0); rshgroup.matrixAutoUpdate = true; rshgroup.updateMatrix(); scenetjs.add(rshgroup); renderertjs.render(scenetjs, cameratjs); } I have a seperate function to load glb models to scene, but i want to correct the positon and scale so it apprear correctly function LoadModels(){ lon0 = (Math.min(...facades_lon)) lat0 = (Math.min(...facades_lat)) lon1 = (Math.max(...facades_lon)) lat1 = (Math.max(...facades_lat)) var zoom = 19; var top_tile = lat2tile(lat1, zoom)-1; var left_tile = lon2tile(lon0, zoom)-1; var bottom_tile = lat2tile(lat0, zoom)+1; var right_tile = lon2tile(lon1, zoom)+1; var width = Math.abs(left_tile - right_tile) + 1; var height = Math.abs(top_tile - bottom_tile) + 1; var resolution = 156543.03 * Math.cos(lat0/180*Math.PI) / (2 ** zoom) *256 // meters/tile var lon0_tile = tile2lon(left_tile, zoom) var lat0_tile = tile2lat(bottom_tile+1, zoom) // bottom_tile+1 to get bottom (left) corner var subdivisions = { 'h' : height, 'w' : width }; console.log(subdivisions); var xmin = -haversineDistance([lat0,lon0],[lat0,lon0_tile]); var zmin = -haversineDistance([lat0,lon0],[lat0_tile,lon0]) ; var xmax = xmin + width*resolution; var zmax = zmin + height*resolution; var twidth=(xmax-xmin)/subdivisions.w; var theight=(zmax-zmin)/subdivisions.h; var mwidth=(xmax-xmin); var mheight=(zmax-zmin); var c=256; var tratio=256/c; var aratio=((c/twidth)+(c/theight))/2; console.log(aratio); var y_offset_facade = 0.01 console.log(facades); facades.rootnodes = {} var i=0; for (const facade of facades){ var x = Math.sign(lon0) * haversineDistance([lat0,lon0],[lat0,facade.facade_lon]) var y = Math.sign(lat0) * haversineDistance([lat0,lon0],[facade.facade_lat,lon0]) facades.rootnodes[facade.id] = new Object(); loadertjs.load( facade.file_url, function ( gltf ) { //gltf.scene.scale.set(facade.scale,facade.scale,facade.scale); //gltf.scene.position.z=y+facade.offset_y; //gltf.scene.position.x=x+facade.offset_x; gltf.scene.position.y=y_offset_facade + facade.offset_z gltf.scene.scale.set(facade.scale*aratio, facade.scale*aratio, facade.scale*aratio); //gltf.scene.translateY= y_offset_facade + facade.offset_z; gltf.scene.rotateY=Math.PI; facades.rootnodes[facade.id].id = facade.id; facades.rootnodes[facade.id].detail_mapajax_url = facade.detail_mapajax_url; gltf.scene.userData = facades.rootnodes[facade.id]; facadegroup.add(gltf.scene) renderertjs.render(scenetjs, cameratjs); } ); i++; } scenetjs.fog = null; renderertjs.fog = null; scenetjs.add( facadegroup ); renderertjs.render(scenetjs, cameratjs); } I want to achieve as show in babylonjs screenshot current situation as shown in threejs screenshot i have corrected scale now it appears on top of each other All models have displacement issue too
I have to implement a rectangle and polygon on maps. Divide the rectangle into grids, remove all the grids which are not a part of the polygon
I have made a map and added a polygon to mark the boundaries of city Gurgaon. I have made a rectangle over the polygon. The task I have pending is that I have to divide this rectangle into 15*15 grids and if any grid doesn't contain any part of Gurgaon, I have to remove the specific grid. I have to display all the grids that contain some part of Gurgaon. I have my code below. How do I implement it? /* Always set the map height explicitly to define the size of the div * element that contains the map. */ #map { height: 100%; } /* Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } <div id="map"></div> <script> var map; function initMap() { map = new google.maps.Map(document.getElementById('map'), { center: { lat: 28.644800, lng: 77.216721 }, zoom: 8 }); var rectangle = new google.maps.Rectangle({ strokeColor: '#FF0000', strokeOpacity: 0.8, strokeWeight: 2, fillColor: '#FF0000', fillOpacity: 0.35, map: map, bounds: { north: 28.543048, south: 28.343115132, east: 77.14079, west: 76.8536989 } }); var kmlAreaData = [ [76.8559524, 28.400185], [76.8696144, 28.3854157], [76.8796311, 28.3957333], [76.8877189, 28.4035728], [76.8925248, 28.4090852], [76.8928393, 28.4087934], [76.893404, 28.4080159], [76.8936004, 28.4068713], [76.8942142, 28.4055972], [76.8944107, 28.403891], [76.8948035, 28.4025736], [76.8951718, 28.4016018], [76.8962766, 28.4008891], [76.8997631, 28.399442], [76.901359, 28.3990533], [76.9035196, 28.3987293], [76.9066622, 28.3984054], [76.9072269, 28.3986213], [76.9186995, 28.3909024], [76.9214918, 28.3909756], [76.9216637, 28.389442], [76.9217213, 28.3885181], [76.9217923, 28.3871392], [76.9260805, 28.3841303], [76.9448805, 28.3725251], [76.9471551, 28.3716597], [76.948573, 28.3709873], [76.9515171, 28.3693086], [76.9522659, 28.3687944], [76.9516175, 28.3671617], [76.9516565, 28.3665591], [76.9523096, 28.3655348], [76.9523001, 28.3651476], [76.9522603, 28.3643684], [76.9523877, 28.3636001], [76.9525249, 28.3622109], [76.9527433, 28.3614269], [76.9532313, 28.3605505], [76.9537915, 28.3600506], [76.9542382, 28.3594186], [76.9545178, 28.3591334], [76.9552684, 28.3585701], [76.9571494, 28.3570005], [76.9561195, 28.3569945], [76.9561191, 28.3564473], [76.9573246, 28.3565013], [76.9573267, 28.356607], [76.9573286, 28.3567014], [76.9583386, 28.3566958], [76.9584105, 28.3610635], [76.9601282, 28.3610604], [76.9638667, 28.3610535], [76.9642001, 28.3610529], [76.9651835, 28.3615682], [76.9655589, 28.3618005], [76.9659736, 28.3620599], [76.9663627, 28.3620514], [76.9671707, 28.3592872], [76.9674586, 28.3587191], [76.9862863, 28.3472344], [77.0043215, 28.3882571], [77.015501, 28.3830638], [77.0144066, 28.3799885], [77.0220731, 28.3737851], [77.0255131, 28.3775172], [77.0361475, 28.3681112], [77.0471875, 28.3583116], [77.1188819, 28.3701088], [77.1146418, 28.3983352], [77.138683, 28.4072441], [77.1287009, 28.4404645], [77.1277292, 28.4428335], [77.1253919, 28.4502107], [77.1242593, 28.4510201], [77.1238974, 28.4550376], [77.1234764, 28.4597104], [77.1200235, 28.4627232], [77.116455, 28.4672235], [77.1124725, 28.4731841], [77.1141826, 28.4735236], [77.1150614, 28.4815648], [77.1167211, 28.4850054], [77.1176759, 28.4869846], [77.119507, 28.495139], [77.1124785, 28.4984532], [77.1101397, 28.4993961], [77.1053975, 28.5013384], [77.1037667, 28.5024887], [77.1023081, 28.5031016], [77.1000336, 28.5038653], [77.0976518, 28.5048552], [77.0957528, 28.5070803], [77.0986588, 28.5114068], [77.0939182, 28.5138682], [77.0932384, 28.5140471], [77.0925943, 28.5142311], [77.0859574, 28.5161025], [77.0846262, 28.5164992], [77.0840906, 28.5166587], [77.0830606, 28.5169698], [77.0827494, 28.5170898], [77.0804111, 28.5180224], [77.0792948, 28.5182048], [77.0792318, 28.5182128], [77.0774387, 28.5184405], [77.075014, 28.5187138], [77.0747744, 28.5188088], [77.0744668, 28.5189307], [77.073365, 28.5195071], [77.0723002, 28.5202589], [77.0722213, 28.5201457], [77.0720942, 28.5193619], [77.0713662, 28.5175938], [77.0709886, 28.5170499], [77.0688651, 28.5145822], [77.067616, 28.5129745], [77.0671624, 28.5119289], [77.0653494, 28.5125045], [77.0645251, 28.5126891], [77.0633996, 28.5126665], [77.0612165, 28.512224], [77.0606606, 28.5122555], [77.0577011, 28.5127924], [77.0575428, 28.5128211], [77.0543531, 28.5143512], [77.0491721, 28.5160453], [77.0462065, 28.5166427], [77.0485082, 28.52092], [77.0446105, 28.5230838], [77.0433705, 28.5241645], [77.0433583, 28.5251547], [77.0431514, 28.5252371], [77.0424046, 28.5256984], [77.0417144, 28.5260711], [77.0356349, 28.5292442], [77.0356102, 28.5292571], [77.0340499, 28.5300715], [77.0321199, 28.5310788], [77.0298669, 28.5316849], [77.0297824, 28.5317014], [77.0245904, 28.5327132], [77.0232547, 28.5335616], [77.0219253, 28.5347748], [77.0217025, 28.5349596], [77.0197006, 28.536218], [77.0174236, 28.537688], [77.0162809, 28.5392112], [77.0140944, 28.5399982], [77.0134571, 28.5405156], [77.0128497, 28.5402697], [77.0122093, 28.5400104], [77.0101934, 28.5402687], [77.0096859, 28.539869], [77.0086334, 28.5403299], [77.0076539, 28.5397729], [77.0054602, 28.5394958], [77.0043849, 28.537795], [77.0016644, 28.5334918], [77.0013296, 28.5309864], [77.0016511, 28.5308084], [77.0050118, 28.528947], [77.0084757, 28.5270284], [77.0096001, 28.526641], [77.0105771, 28.5257745], [77.0109605, 28.5254514], [77.0146291, 28.5243485], [77.0169637, 28.5210774], [77.0144723, 28.518757], [77.012884, 28.5172777], [77.0125538, 28.5169701], [77.0116413, 28.5161518], [77.0108262, 28.5154209], [77.0105264, 28.5151521], [77.010493, 28.5151221], [77.0100507, 28.5147255], [77.0085304, 28.5151415], [77.0070407, 28.515549], [77.0067265, 28.515635], [77.0059192, 28.5158559], [77.0027227, 28.5172931], [76.9999924, 28.5179713], [76.9990215, 28.5184832], [76.9968081, 28.5197124], [76.9959691, 28.5180316], [76.9953859, 28.5171445], [76.9945276, 28.5176083], [76.9909463, 28.5135677], [76.983382, 28.5169891], [76.9832794, 28.5170355], [76.9812139, 28.5191174], [76.9802327, 28.5198029], [76.9783615, 28.5212481], [76.9760102, 28.5189385], [76.9706299, 28.515795], [76.9620491, 28.5115276], [76.9648243, 28.509661], [76.9762078, 28.4984432], [76.9720448, 28.4914468], [76.9692444, 28.4860356], [76.9665067, 28.4806004], [76.9654443, 28.4787172], [76.9642444, 28.4765902], [76.9607315, 28.4705733], [76.9581786, 28.468231], [76.9537542, 28.4642338], [76.9453408, 28.4568557], [76.9465866, 28.4442302], [76.9069895, 28.4252864], [76.8616645, 28.4410796], [76.8671319, 28.4182734], [76.869179, 28.4060588], [76.8559524, 28.400185] ]; var coords = buildCoordinatesArrayFromString(kmlAreaData); function buildCoordinatesArrayFromString(MultiGeometryCoordinates) { var finalData = []; MultiGeometryCoordinates.forEach(function(item, i) { finalData.push({ lng: parseFloat(item[0]), lat: parseFloat(item[1]) }); }); return finalData; } var polygon = new google.maps.Polygon({ paths: coords, strokeColor: '#0037FF', strokeOpacity: 0.8, strokeWeight: 3 }); polygon.setMap(map); } </script> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBdPTn6zjYe28lYdTxA_76YPhebeKlmPIQ&callback=initMap"></script>
create your 15x15 grid. Following function is modified from an answer to Grid on top of Google maps produces gaps in squares function drawRects(bounds, verticalBlocks, horizontalBlocks) { var startingLatLng = new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getSouthWest().lng()); var NWcorner = new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getSouthWest().lng()); var SWcorner = bounds.getSouthWest(); var verticalBlockSize = google.maps.geometry.spherical.computeDistanceBetween(NWcorner, SWcorner) / verticalBlocks; var NEcorner = bounds.getNorthEast(); var horizontalBlockSize = google.maps.geometry.spherical.computeDistanceBetween(NWcorner, NEcorner) / verticalBlocks; // modified from https://stackoverflow.com/questions/38493098/grid-on-top-of-google-maps-produces-gaps-in-squares/38494172#38494172 var NW = startingLatLng; // define horizontal lines var longitudes = []; longitudes.push(NW.lng()); for (var i = 0; i < verticalBlocks; i++) { var longitude = google.maps.geometry.spherical.computeOffset(NW, horizontalBlockSize, 90).lng(); longitudes.push(longitude) NW = new google.maps.LatLng(NW.lat(), longitude); } var NW = startingLatLng; // for each longitude, make a column of squares for (var i = 0; i < longitudes.length - 1; i++) { NW = new google.maps.LatLng(startingLatLng.lat(), longitudes[i]); for (var j = 0; j < horizontalBlocks; j++) { var north = NW.lat(); var south = google.maps.geometry.spherical.computeOffset(NW, verticalBlockSize, 180).lat(); var east = longitudes[i + 1]; var west = longitudes[i]; var corner1 = new google.maps.LatLng(north, east); // NE var corner2 = new google.maps.LatLng(south, east); // SE var corner3 = new google.maps.LatLng(south, west); // SW var corner4 = new google.maps.LatLng(north, west); // NW var polygon = new google.maps.Polygon({ strokeColor: "#FF0000", strokeOpacity: 0.25, strokeWeight: 2, fillColor: "#FF0000", fillOpacity: 0.1, map: map, paths: [corner1, corner2, corner3, corner4] }); polygons.push(polygon); NW = new google.maps.LatLng(south, longitudes[i]); } } } process through those rectangles and determine whether they contain any part of the input polygon // find boundary squares for (var i = 0; i < polygons.length; i++) { var hidePoly = true; for (var j = 0; j < polygon.getPath().getLength(); j++) { if (google.maps.geometry.poly.containsLocation(polygon.getPath().getAt(j), polygons[i])) { console.log("polygons[" + i + "] contains " + polygon.getPath().getAt(j)); hidePoly = false break; } } if (hidePoly) polygons[i].setMap(null); } // find internal squares for (var j = 0; j < polygon.getPath().getLength(); j++) { for (var i = 0; i < polygons.length; i++) { for (var k = 0; k < polygons[i].getPath().getLength(); k++) { if (google.maps.geometry.poly.containsLocation(polygons[i].getPath().getAt(k), polygon)) { console.log("polygon contains polygons[" + i + "]=" + polygons[i].getPath().getAt(k)); polygons[i].setMap(map) break; } } } } proof of concept fiddle code snippet: html, body, #map { height: 100%; margin: 0; padding: 0; } <div id="map"></div> <script> var map; function initMap() { map = new google.maps.Map(document.getElementById('map'), { center: { lat: 28.644800, lng: 77.216721 }, zoom: 8 }); var rectangle = new google.maps.Rectangle({ strokeColor: '#0000FF', strokeOpacity: 0.8, strokeWeight: 2, fillColor: '#0000FF', fillOpacity: 0.2, map: map, bounds: { north: 28.543048, south: 28.343115132, east: 77.14079, west: 76.8536989 } }); var kmlAreaData = [[76.8559524, 28.400185],[76.8696144, 28.3854157],[76.8796311, 28.3957333],[76.8877189, 28.4035728],[76.8925248, 28.4090852],[76.8928393, 28.4087934],[76.893404, 28.4080159],[76.8936004, 28.4068713],[76.8942142, 28.4055972],[76.8944107, 28.403891],[76.8948035, 28.4025736],[76.8951718, 28.4016018],[76.8962766, 28.4008891],[76.8997631, 28.399442],[76.901359, 28.3990533],[76.9035196, 28.3987293],[76.9066622, 28.3984054],[76.9072269, 28.3986213],[76.9186995, 28.3909024],[76.9214918, 28.3909756],[76.9216637, 28.389442],[76.9217213, 28.3885181],[76.9217923, 28.3871392],[76.9260805, 28.3841303],[76.9448805, 28.3725251],[76.9471551, 28.3716597],[76.948573, 28.3709873],[76.9515171, 28.3693086],[76.9522659, 28.3687944],[76.9516175, 28.3671617],[76.9516565, 28.3665591],[76.9523096, 28.3655348],[76.9523001, 28.3651476],[76.9522603, 28.3643684],[76.9523877, 28.3636001],[76.9525249, 28.3622109],[76.9527433, 28.3614269],[76.9532313, 28.3605505],[76.9537915, 28.3600506],[76.9542382, 28.3594186],[76.9545178, 28.3591334],[76.9552684, 28.3585701],[76.9571494, 28.3570005],[76.9561195, 28.3569945],[76.9561191, 28.3564473],[76.9573246, 28.3565013],[76.9573267, 28.356607],[76.9573286, 28.3567014],[76.9583386, 28.3566958],[76.9584105, 28.3610635],[76.9601282, 28.3610604],[76.9638667, 28.3610535],[76.9642001, 28.3610529],[76.9651835, 28.3615682],[76.9655589, 28.3618005],[76.9659736, 28.3620599],[76.9663627, 28.3620514],[76.9671707, 28.3592872],[76.9674586, 28.3587191],[76.9862863, 28.3472344],[77.0043215, 28.3882571],[77.015501, 28.3830638],[77.0144066, 28.3799885],[77.0220731, 28.3737851],[77.0255131, 28.3775172],[77.0361475, 28.3681112],[77.0471875, 28.3583116],[77.1188819, 28.3701088],[77.1146418, 28.3983352],[77.138683, 28.4072441],[77.1287009, 28.4404645],[77.1277292, 28.4428335],[77.1253919, 28.4502107],[77.1242593, 28.4510201],[77.1238974, 28.4550376],[77.1234764, 28.4597104],[77.1200235, 28.4627232],[77.116455, 28.4672235],[77.1124725, 28.4731841],[77.1141826, 28.4735236],[77.1150614, 28.4815648],[77.1167211, 28.4850054],[77.1176759, 28.4869846],[77.119507, 28.495139],[77.1124785, 28.4984532],[77.1101397, 28.4993961],[77.1053975, 28.5013384],[77.1037667, 28.5024887],[77.1023081, 28.5031016],[77.1000336, 28.5038653],[77.0976518, 28.5048552],[77.0957528, 28.5070803],[77.0986588, 28.5114068],[77.0939182, 28.5138682],[77.0932384, 28.5140471],[77.0925943, 28.5142311],[77.0859574, 28.5161025],[77.0846262, 28.5164992],[77.0840906, 28.5166587],[77.0830606, 28.5169698],[77.0827494, 28.5170898],[77.0804111, 28.5180224],[77.0792948, 28.5182048],[77.0792318, 28.5182128],[77.0774387, 28.5184405],[77.075014, 28.5187138],[77.0747744, 28.5188088],[77.0744668, 28.5189307],[77.073365, 28.5195071],[77.0723002, 28.5202589],[77.0722213, 28.5201457],[77.0720942, 28.5193619],[77.0713662, 28.5175938],[77.0709886, 28.5170499],[77.0688651, 28.5145822],[77.067616, 28.5129745],[77.0671624, 28.5119289],[77.0653494, 28.5125045],[77.0645251, 28.5126891],[77.0633996, 28.5126665],[77.0612165, 28.512224],[77.0606606, 28.5122555],[77.0577011, 28.5127924],[77.0575428, 28.5128211],[77.0543531, 28.5143512],[77.0491721, 28.5160453],[77.0462065, 28.5166427],[77.0485082, 28.52092],[77.0446105, 28.5230838],[77.0433705, 28.5241645],[77.0433583, 28.5251547],[77.0431514, 28.5252371],[77.0424046, 28.5256984],[77.0417144, 28.5260711],[77.0356349, 28.5292442],[77.0356102, 28.5292571],[77.0340499, 28.5300715],[77.0321199, 28.5310788],[77.0298669, 28.5316849],[77.0297824, 28.5317014],[77.0245904, 28.5327132],[77.0232547, 28.5335616],[77.0219253, 28.5347748],[77.0217025, 28.5349596],[77.0197006, 28.536218],[77.0174236, 28.537688],[77.0162809, 28.5392112],[77.0140944, 28.5399982],[77.0134571, 28.5405156],[77.0128497, 28.5402697],[77.0122093, 28.5400104],[77.0101934, 28.5402687],[77.0096859, 28.539869],[77.0086334, 28.5403299],[77.0076539, 28.5397729],[77.0054602, 28.5394958],[77.0043849, 28.537795],[77.0016644, 28.5334918],[77.0013296, 28.5309864],[77.0016511, 28.5308084],[77.0050118, 28.528947],[77.0084757, 28.5270284],[77.0096001, 28.526641],[77.0105771, 28.5257745],[77.0109605, 28.5254514],[77.0146291, 28.5243485],[77.0169637, 28.5210774],[77.0144723, 28.518757],[77.012884, 28.5172777],[77.0125538, 28.5169701],[77.0116413, 28.5161518],[77.0108262, 28.5154209],[77.0105264, 28.5151521],[77.010493, 28.5151221],[77.0100507, 28.5147255],[77.0085304, 28.5151415],[77.0070407, 28.515549],[77.0067265, 28.515635],[77.0059192, 28.5158559],[77.0027227, 28.5172931],[76.9999924, 28.5179713],[76.9990215, 28.5184832],[76.9968081, 28.5197124],[76.9959691, 28.5180316],[76.9953859, 28.5171445],[76.9945276, 28.5176083],[76.9909463, 28.5135677],[76.983382, 28.5169891],[76.9832794, 28.5170355],[76.9812139, 28.5191174],[76.9802327, 28.5198029],[76.9783615, 28.5212481],[76.9760102, 28.5189385],[76.9706299, 28.515795],[76.9620491, 28.5115276],[76.9648243, 28.509661],[76.9762078, 28.4984432],[76.9720448, 28.4914468],[76.9692444, 28.4860356],[76.9665067, 28.4806004],[76.9654443, 28.4787172],[76.9642444, 28.4765902],[76.9607315, 28.4705733],[76.9581786, 28.468231],[76.9537542, 28.4642338],[76.9453408, 28.4568557],[76.9465866, 28.4442302],[76.9069895, 28.4252864],[76.8616645, 28.4410796],[76.8671319, 28.4182734],[76.869179, 28.4060588],[76.8559524, 28.400185]]; var coords = buildCoordinatesArrayFromString(kmlAreaData); function buildCoordinatesArrayFromString(MultiGeometryCoordinates) { var finalData = []; MultiGeometryCoordinates.forEach(function(item, i) { finalData.push({ lng: parseFloat(item[0]), lat: parseFloat(item[1]) }); }); return finalData; } var polygon = new google.maps.Polygon({ paths: coords, strokeColor: '#0037FF', strokeOpacity: 0.8, strokeWeight: 3 }); polygon.setMap(map); drawRects(rectangle.getBounds(), 15, 15); map.fitBounds(rectangle.getBounds()); // find boundary squares for (var i = 0; i < polygons.length; i++) { var hidePoly = true; for (var j = 0; j < polygon.getPath().getLength(); j++) { if (google.maps.geometry.poly.containsLocation(polygon.getPath().getAt(j), polygons[i])) { console.log("polygons[" + i + "] contains " + polygon.getPath().getAt(j)); hidePoly = false break; } } if (hidePoly) polygons[i].setMap(null); } // find internal squares for (var j = 0; j < polygon.getPath().getLength(); j++) { for (var i = 0; i < polygons.length; i++) { for (var k = 0; k < polygons[i].getPath().getLength(); k++) { if (google.maps.geometry.poly.containsLocation(polygons[i].getPath().getAt(k), polygon)) { console.log("polygon contains polygons[" + i + "]=" + polygons[i].getPath().getAt(k)); polygons[i].setMap(map) break; } } } } } var polygons = []; function drawRects(bounds, verticalBlocks, horizontalBlocks) { var startingLatLng = new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getSouthWest().lng()); var NWcorner = new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getSouthWest().lng()); var SWcorner = bounds.getSouthWest(); var verticalBlockSize = google.maps.geometry.spherical.computeDistanceBetween(NWcorner, SWcorner) / verticalBlocks; var NEcorner = bounds.getNorthEast(); var horizontalBlockSize = google.maps.geometry.spherical.computeDistanceBetween(NWcorner, NEcorner) / verticalBlocks; var width = 10; var height = 10; // modified from https://stackoverflow.com/questions/38493098/grid-on-top-of-google-maps-produces-gaps-in-squares/38494172#38494172 var bounds; var NW = startingLatLng; // define horizontal lines var longitudes = []; longitudes.push(NW.lng()); for (var i = 0; i < verticalBlocks; i++) { var longitude = google.maps.geometry.spherical.computeOffset(NW, horizontalBlockSize, 90).lng(); longitudes.push(longitude) NW = new google.maps.LatLng(NW.lat(), longitude); } var NW = startingLatLng; // for each longitude, make a column of squares for (var i = 0; i < longitudes.length - 1; i++) { NW = new google.maps.LatLng(startingLatLng.lat(), longitudes[i]); for (var j = 0; j < horizontalBlocks; j++) { var north = NW.lat(); var south = google.maps.geometry.spherical.computeOffset(NW, verticalBlockSize, 180).lat(); var east = longitudes[i + 1]; var west = longitudes[i]; var corner1 = new google.maps.LatLng(north, east); // NE var corner2 = new google.maps.LatLng(south, east); // SE var corner3 = new google.maps.LatLng(south, west); // SW var corner4 = new google.maps.LatLng(north, west); // NW var polygon = new google.maps.Polygon({ strokeColor: "#FF0000", strokeOpacity: 0.25, strokeWeight: 2, fillColor: "#FF0000", fillOpacity: 0.1, map: map, paths: [corner1, corner2, corner3, corner4] }); polygons.push(polygon); NW = new google.maps.LatLng(south, longitudes[i]); } } } </script> <script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyBdPTn6zjYe28lYdTxA_76YPhebeKlmPIQ&callback=initMap"></script>
Leaflet.js: Pass feature property to class constructor
I have quite a few used-defined svg markers (glyphs) (big thanks to the SO user with the name rioV8 for his help on this - and not only this -...) and ideally I would like these glyphs to get their shape from the feature properties structure. //create feature properties var p = { "id": i, "popup": "Dot_" + i, "year": parseInt(data[i].year), "glyphName": "square", "size": 500 // Fixed size circle radius=~13 }; These user-defined glyphs extend L.circleMarker and for simplicity let's say that their shapes can be square or diamond. Currently, I am extending L.Class and am passing glyphName in the constructor: (feel free to criticise that, If it doesnt look nice to you) var glyph = L.Class.extend({ initialize: function(glyphName) { glyphName === "square"? this.type = MarkerSquare: glyphName === "diamond"? this.type = MarkerDiamond: this.type = L.circleMarker; }, }); and when I need to plot the glyphs I have something like: L.geoJson(myDots[i], { pointToLayer: function(feature, latlng) { var p = latlng; var myGlyph = new glyph('diamond') return new myGlyph.type(p, style(feature)); }, onEachFeature: onEachDot }).addTo(map); Can I have the shape determined by the feature properties please? Eventually, what i am trying to achieve is to merge these two lines var myGlyph = new glyph('diamond') return new myGlyph.type(p, style(feature)); to something like return new myGlyph.type(p, style(feature)); That will enable me to plot different shapes, and those shapes will be determined by the input data used to populate features properties. In a similar manner that these properties are used for color or size they could now be used to set the shape. Thanks! (Full code below) <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Chart</title> <style> html, body { height: 100%; margin: 0; } #map { width: 600px; height: 600px; } </style> <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script> <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script> <link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.3/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet#1.3.3/dist/leaflet.js"></script> </head> <body> <div id="map"></div> <script> L.Canvas.include({ _updateMarkerDiamond: function(layer) { if (!this._drawing || layer._empty()) { return; } var p = layer._point, ctx = this._ctx, r = Math.max(Math.round(layer._radius), 6); this._drawnLayers[layer._leaflet_id] = layer; ctx.beginPath(); ctx.moveTo(p.x - r, p.y); ctx.lineTo(p.x, p.y - r); ctx.lineTo(p.x + r, p.y); ctx.lineTo(p.x, p.y + r); ctx.lineTo(p.x - r, p.y); ctx.closePath(); this._fillStroke(ctx, layer); } }); var MarkerDiamond = L.CircleMarker.extend({ _updatePath: function() { this._renderer._updateMarkerDiamond(this); } }); L.Canvas.include({ _updateMarkerSquare: function(layer) { if (!this._drawing || layer._empty()) { return; } var p = layer._point, ctx = this._ctx, r = Math.max(Math.round(layer._radius), 5); this._drawnLayers[layer._leaflet_id] = layer; ctx.beginPath(); ctx.moveTo(p.x - r, p.y - r); ctx.lineTo(p.x + r, p.y - r); ctx.lineTo(p.x + r, p.y + r); ctx.lineTo(p.x - r, p.y + r); ctx.lineTo(p.x - r, p.y - r); ctx.closePath(); this._fillStroke(ctx, layer); } }); var MarkerSquare = L.CircleMarker.extend({ _updatePath: function() { this._renderer._updateMarkerSquare(this); } }); var glyph = L.Class.extend({ initialize: function(glyphName) { glyphName === "square"? this.type = MarkerSquare: glyphName === "diamond"? this.type = MarkerDiamond: this.type = L.circleMarker; }, }); var data = []; var NumOfPoints = 100; for (let i = 0; i < NumOfPoints; i++) { data.push({ num: i, x: Math.random() * 60, y: Math.random() * 60, year: Math.floor(100 * Math.random()) }) } renderChart(data); function make_dots(data) { var arr = []; var nest = d3.nest() .key(function(d) { return Math.floor(d.year / 10); }) .entries(data); for (var k = 0; k < nest.length; ++k) { arr[k] = helper(nest[k].values); } return arr; } function helper(data) { dots = { type: "FeatureCollection", features: [] }; for (var i = 0; i < data.length; ++i) { x = data[i].x; y = data[i].y; var g = { "type": "Point", "coordinates": [x, y] }; //create feature properties var p = { "id": i, "popup": "Dot_" + i, "year": parseInt(data[i].year), //"glyphName": "square", "size": 500 // Fixed size circle radius=~13 }; //create features with proper geojson structure dots.features.push({ "geometry": g, "type": "Feature", "properties": p }); } return dots; } //create color ramp function getColor(y) { return y > 90 ? '#6068F0' : y > 80 ? '#6B64DC' : y > 70 ? '#7660C9' : y > 60 ? '#815CB6' : y > 50 ? '#8C58A3' : y > 40 ? '#985490' : y > 30 ? '#A3507C' : y > 20 ? '#AE4C69' : y > 10 ? '#B94856' : y > 0 ? '#C44443' : '#D04030'; } //calculate radius so that resulting circles will be proportional by area function getRadius(y) { r = Math.sqrt(y / Math.PI) return r; } var myRenderer; //create style, with fillColor picked from color ramp function style(feature) { return { radius: getRadius(feature.properties.size), fillColor: getColor(feature.properties.year), color: "#000", weight: 0, opacity: 1, fillOpacity: 0.9, renderer: myRenderer }; } //create highlight style, with darker color and larger radius function highlightStyle(feature) { return { radius: getRadius(feature.properties.size) + 1.5, fillColor: "#FFCE00", color: "#FFCE00", weight: 1, opacity: 1, fillOpacity: 0.9, }; } //attach styles and popups to the marker layer function highlightDot(e) { var layer = e.target; dotStyleHighlight = highlightStyle(layer.feature); layer.setStyle(dotStyleHighlight); if (!L.Browser.ie && !L.Browser.opera) { layer.bringToFront(); } } function resetDotHighlight(e) { var layer = e.target; dotStyleDefault = style(layer.feature); layer.setStyle(dotStyleDefault); } function onEachDot(feature, layer) { layer.on({ mouseover: highlightDot, mouseout: resetDotHighlight }); var popup = '<table style="width:110px"><tbody><tr><td><div><b>Marker:</b></div></td><td><div>' + feature.properties.popup + '</div></td></tr><tr class><td><div><b>Group:</b></div></td><td><div>' + feature.properties.glyphName + '</div></td></tr><tr><td><div><b>X:</b></div></td><td><div>' + feature.geometry.coordinates[0] + '</div></td></tr><tr><td><div><b>Y:</b></div></td><td><div>' + feature.geometry.coordinates[1] + '</div></td></tr></tbody></table>' layer.bindPopup(popup); } function renderChart(data) { var myDots = make_dots(data); var minZoom = 0, maxZoom = 15; var map = L.map('map', { minZoom: minZoom, maxZoom: maxZoom }).setView([30, 30], 3); L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { continuousWorld: false, minZoom: 0, noWrap: true }).addTo(map); myRenderer = L.canvas({ padding: 0.5 }); // Define an array to keep layerGroups var dotlayer = []; //create marker layer and display it on the map for (var i = 0; i < myDots.length; i += 1) { dotlayer[i] = L.geoJson(myDots[i], { pointToLayer: function(feature, latlng) { var p = latlng; var myGlyph = new glyph('diamond') return new myGlyph.type(p, style(feature)); }, onEachFeature: onEachDot }).addTo(map); } var cl = L.control.layers(null, {}).addTo(map); for (j = 0; j < dotlayer.length; j += 1) { var name = "Group " + j + "0-" + j + "9"; cl.addOverlay(dotlayer[j], name); } } </script> </body> </html>
You need to make the shape of the marker a property of the marker and merge the render parts of the MarkerDiamond and MarkerSquare into a different marker and decide which render part to draw with an if inside the _updateMarkerXX method based on the property shape. layer.options.shape contains the shape inside the render routine. Or do it in the Marker routine var Marker = L.CircleMarker.extend({ _updatePath: function() { if (this.options.shape === "square") this._renderer._updateMarkerSquare(this); if (this.options.shape === "diamond") this._renderer._updateMarkerDiamond(this); } }); function style(feature) { return { radius: getRadius(feature.properties.size), shape: feature.properties.shape, fillColor: getColor(feature.properties.year), color: "#000", weight: 0, opacity: 1, fillOpacity: 0.9, renderer: myRenderer }; } Edit It might be useful to time the use of Magic Numbers (Enums) instead of strings, because the compare of a number is cheaper than string compare. And Aenaon has about 300K markers, but it might be negligible.
Adding value on button click
I have a script which on button click finds the closest location to you by searching an array, it then finds the corresponding entry in another array and adds a number to that array. However I am having issues with appending the value to it. Instead of starting with "0" and then adding 1 making it "1" it comes up like this "01" I thought it was because it was a string to I tried to parse it as an Int this didn't work. I think it may be a problem with the way I am initially setting values in the array, I made a For loop to set the first 200 values to 0, though I only want this to run once then as the button is clicked it appends the number that is typed in to a text field. I am unsure on how to progress with this issue. Code below: I have tried to highlight the important bits of my code where I think the problem lies. Screenshot of problem (rather than adding the value I put in the box it just attaches it to the end as if it were a string): jQuery(function($){ var Loc1; var Loc2; var service; var marker = []; var pos; var infowindow; var placeLoc **var j;** **var markerValue = [];** **for (j = 0; j<200; j++ ){ markerValue[j] = 0; }** var markers; var x = 0, y = 0, vx = 0, vy = 0, ax = 0, ay = 0; var points; var sphere = document.getElementById("sphere"); var counting = false; var counter = 0; var numberOne; if (window.DeviceMotionEvent != undefined) { window.ondevicemotion = function(e) { ax = event.accelerationIncludingGravity.x * 5; ay = event.accelerationIncludingGravity.y * 5; document.getElementById("counterSpan").innerHTML = Math.round(counter*10)/10; //document.getElementById("accelerationX").innerHTML = Math.round(e.accelerationIncludingGravity.x * 10)/10; //document.getElementById("accelerationY").innerHTML = Math.round(e.accelerationIncludingGravity.y * 10)/10; //document.getElementById("accelerationZ").innerHTML = Math.round(e.accelerationIncludingGravity.z * 10)/10; var moveX = Math.round(e.accelerationIncludingGravity.x * 10)/10; //var moveY = Math.round(e.accelerationIncludingGravity.y * 10)/10; //var moveZ = Math.round(e.accelerationIncludingGravity.z * 10)/10; if(moveX > 5 || moveX < -5) { counting = true; //alert(counting); if(counter < 100){counter+=0.01;} } if ( e.rotationRate ) { //document.getElementById("rotationAlpha").innerHTML = Math.round(e.rotationRate.alpha * 10)/10; //document.getElementById("rotationBeta").innerHTML = Math.round(e.rotationRate.beta * 10)/10; //document.getElementById("rotationGamma").innerHTML = Math.round(e.rotationRate.gamma * 10)/10; } } setInterval( function() { var landscapeOrientation = window.innerWidth/window.innerHeight > 1; if ( landscapeOrientation) { vx = vx + ay; vy = vy + ax; } else { vy = vy - ay; vx = vx + ax; } vx = vx * 0.98; vy = vy * 0.98; y = parseInt(y + vy / 50); x = parseInt(x + vx / 50); boundingBoxCheck(); //sphere.style.top = y + "px"; //sphere.style.left = x + "px"; }, 25); } function boundingBoxCheck(){ if (x<0) { x = 0; vx = -vx; } if (y<0) { y = 0; vy = -vy; } if (x>document.documentElement.clientWidth-20) { x = document.documentElement.clientWidth-20; vx = -vx; } if (y>document.documentElement.clientHeight-20) { y = document.documentElement.clientHeight-20; vy = -vy; } } function minmax(value, min, max) { if(parseInt(value) < 0 || isNaN(value)) return 0; else if(parseInt(value) > 100) return 100; else return value; } var $overlay = $('.overlay'), resize = true, map; function initialize() { /*var mapOptions = { zoom: 8, center: new google.maps.LatLng(-34.397, 150.644), mapTypeId: google.maps.MapTypeId.ROADMAP }; map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions); }*/ var mapOptions = { zoom: 15 }; map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions); // Try HTML5 geolocation if(navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { var pos = new google.maps.LatLng(position.coords.latitude,position.coords.longitude); var request = { location:pos, center:pos, radius:1000, }; infowindow = new google.maps.InfoWindow(); var service = new google.maps.places.PlacesService(map); service.nearbySearch(request,callback); pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); infowindow = new google.maps.InfoWindow({ map: map, position: pos, content: 'You Are Here' }); $('#btnEnergize').data('pos',pos); map.setCenter(pos); }, function() { handleNoGeolocation(true); }); } else { // Browser doesn't support Geolocation handleNoGeolocation(false); } function callback(results, status) { markers = []; if (status == google.maps.places.PlacesServiceStatus.OK) { for (var i = 0; i < results.length; i++) { markers.push(createMarker(results[i])); } } $('#btnEnergize').data('markers',markers); } } function createMarker(place) { placeLoc = place.geometry.location; var marker = new google.maps.Marker({ map: map, position: place.geometry.location, icon: { path: google.maps.SymbolPath.CIRCLE, scale: 8, fillColor:'00a14b', fillOpacity:0.3, fillStroke: '00a14b', strokeWeight:4, strokeOpacity: 0.7 }, }); google.maps.event.addListener(marker, 'click', function() { infowindow.setContent(place.name); infowindow.open(map, this); }); return marker; } function handleNoGeolocation(errorFlag) { if (errorFlag) { var content = 'Error: The Geolocation service failed.'; } else { var content = 'Error: Your browser doesn\'t support geolocation.'; } var options = { map: map, position: new google.maps.LatLng(60, 105), content: content }; var infowindow = new google.maps.InfoWindow(options); map.setCenter(options.position); } google.maps.event.addDomListener(window, 'load', initialize); $('#show').click(function(){ $overlay.show(); if ( resize ){ google.maps.event.trigger(map, 'resize'); resize = false; } }); $('.overlay-bg').click(function(){ $overlay.hide(); }); **$( "#btnEnergize" ).click(function() { var pos = $(this).data('pos'), markers = $(this).data('markers'), closest; if(!pos || !markers){ return; } $.each(markers,function(){ var distance=google.maps.geometry.spherical .computeDistanceBetween(this.getPosition(),pos); if(!closest || closest.distance > distance){ closest={marker:this, distance:distance} } }); if(closest){ //closest.marker will be the nearest marker, do something with it //here we simply trigger a click, which will open the InfoWindow google.maps.event.trigger(closest.marker,'click') Loc2 = closest.marker.getPosition(); numberOne = $("#numberOne").val(); alert(numberOne); localStorage.setItem('points',numberOne); for (var i=0; i<markers.length; i++) { Loc1 = markers[i].getPosition(); if (Loc1.equals(Loc2) /*&& numberOne <= counter*/){ console.log("marker array Location 1 " + Loc1 + " marker " + i); console.log("closest Location 2 " +Loc2); counter -= numberOne; markerValue[i] += numberOne; console.log( "marker " + i + " now equals " + markerValue[i]); } } } alert (markerValue[19]); });** });// JavaScript Document
From skimming over the code it looks like you are retrieving the value here: numberOne = $("#numberOne").val(); that should be numberOne = parseInt($("#numberOne").val()); You use parseInt in the function minmax(), but that one is not in use. If this was the one you used to parse the value to int it has one flaw in that it will only return parsed value on 0 and 100: function minmax(value, min, max) { // Flip these two, CHECK FOR isNaN first. if(parseInt(value) < 0 || isNaN(value)) return 0; else if(parseInt(value) > 100) return 100; else return value; // Here you return value, not parseInt(value) }
OpenLayers: Rotate image with ModifyFeature as Polygon or other
I need to allow user to rotate bitmap features on map with OpenLayers.Control.ModifyFeature or another way as it work for Polygons or other geometry objects, except Point, but only for Point I can set "externalGraphic" with my bitmap. Example of ModifyFeature to rotation as I expected here: ModifyFeature example When I add Vector with Point geometry and activate ModifyFeature there is no rotation tool showing - only drag-drop. I know what is a point, but I need to have tool for rotate bitmap features. It may be image on any another geometry object, but with custom image.
After long research I found an example in gis.stackexchange.com, and fix it. Here is a code which works for me: OpenLayers.Control.RotateGraphicFeature = OpenLayers.Class(OpenLayers.Control.ModifyFeature, { rotateHandleStyle: null, initialize: function(layer, options) { OpenLayers.Control.ModifyFeature.prototype.initialize.apply(this, arguments); this.mode = OpenLayers.Control.ModifyFeature.ROTATE; // This control can only be used to rotate the feature this.geometryTypes = ['OpenLayers.Geometry.Point'] // This control can only be used to rotate point because the 'exteralGraphic' is a point style property var init_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.select); this.rotateHandleStyle = OpenLayers.Util.extend(init_style, { externalGraphic: "./static/resources/images/cross.png", graphicWidth: 12, graphicHeight: 12, fillOpacity: 1 }); }, resetVertices: function() { // You need to set yours renderIntent or use "vertex" if (this.feature && this.feature.renderIntent == "vertex") { var vertex = this.feature; this.feature = this.backup_feature; this.layer.destroyFeatures([this.radiusHandle], {silent: true}); this.collectRadiusHandle(); return; } if (this.dragControl.feature) { this.dragControl.outFeature(this.dragControl.feature); } if (this.vertices.length > 0) { this.layer.removeFeatures(this.vertices, {silent: true}); this.vertices = []; } if (this.virtualVertices.length > 0) { this.layer.removeFeatures(this.virtualVertices, {silent: true}); this.virtualVertices = []; } if (this.dragHandle) { this.layer.destroyFeatures([this.dragHandle], {silent: true}); this.dragHandle = null; } if (this.radiusHandle) { this.layer.destroyFeatures([this.radiusHandle], {silent: true}); this.radiusHandle = null; } if (this.feature && this.feature.geometry && this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) { this.collectDragHandle(); } if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | OpenLayers.Control.ModifyFeature.RESIZE))) { this.collectRadiusHandle(); } if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) { if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) { this.collectVertices(); } } } this.collectRadiusHandle(); }, collectRadiusHandle: function() { var scope = this, feature = this.feature, geometry = this.feature.geometry || this.backup_feature.geometry, centroid = geometry.getCentroid().transform(this.WGS84_google_mercator, this.WGS84), lon = centroid.x, lat = centroid.y; if (this.feature.geometry) { this.backup_feature = this.feature; } else { this.feature.geometry = this.backup_feature.geometry; } var originGeometry = new OpenLayers.Geometry.Point(lon, lat); // radius geometry position. var pixel_dis_x = 10, pixel_dis_y = -10; var rotationFeatureGeometry = new OpenLayers.Geometry.Point(lon+pixel_dis_x, lat+pixel_dis_y); var rotationFeature = new OpenLayers.Feature.Vector(rotationFeatureGeometry, null, this.rotateHandleStyle); var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE); var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE); var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE); rotationFeatureGeometry.move = function(x, y) { OpenLayers.Geometry.Point.prototype.move.call(this, x, y); var dx1 = this.x - originGeometry.x; var dy1 = this.y - originGeometry.y; var dx0 = dx1 - x; var dy0 = dy1 - y; if (rotate) { var a0 = Math.atan2(dy0, dx0); var a1 = Math.atan2(dy1, dx1); var angle = a1 - a0; angle *= 180 / Math.PI; var old_angle = feature.attributes.angle; var new_angle = old_angle - angle; feature.attributes.angle = new_angle; // redraw the feature scope.feature.layer.redraw.call(scope.feature.layer); } }; rotationFeature._sketch = true; this.radiusHandle = rotationFeature; this.radiusHandle.renderIntent = this.vertexRenderIntent; this.layer.addFeatures([this.radiusHandle], {silent: true}); }, CLASS_NAME: "OpenLayers.Control.RotateGraphicFeature" }); Style of your Vector may be as: new OpenLayers.StyleMap({ "default": new OpenLayers.Style({ externalGraphic: "link/to/icon", graphicHeight: "32px", graphicWidth: "25px", fillOpacity: 1, rotation: "${angle}", graphicZIndex: 1 }) })
UPD: I fixed it for OpenLayers 2.13.1 OpenLayers.Control.RotateGraphicFeature = OpenLayers.Class(OpenLayers.Control.ModifyFeature, { rotateHandleStyle: null, initialize: function (layer, options) { OpenLayers.Control.ModifyFeature.prototype.initialize.apply(this, arguments); this.mode = OpenLayers.Control.ModifyFeature.ROTATE; // This control can only be used to rotate the feature this.geometryTypes = ['OpenLayers.Geometry.Point'] // This control can only be used to rotate point because the 'exteralGraphic' is a point style property var init_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.select); this.rotateHandleStyle = OpenLayers.Util.extend(init_style, { externalGraphic: "./static/resources/images/cross.png", graphicWidth: 12, graphicHeight: 12, fillOpacity: 1 }); }, resetVertices: function () { // You need to set yours renderIntent or use "vertex" if (this.feature && this.feature.renderIntent == "vertex") { var vertex = this.feature; this.feature = this.backup_feature; if (this.dragControl.feature) { this.dragControl.outFeature(this.dragControl.feature); } this.layer.destroyFeatures([this.radiusHandle], {silent: true}); delete this.radiusHandle; this.collectRadiusHandle(); return; } if (this.vertices.length > 0) { this.layer.removeFeatures(this.vertices, {silent: true}); this.vertices = []; } if (this.virtualVertices.length > 0) { this.layer.removeFeatures(this.virtualVertices, {silent: true}); this.virtualVertices = []; } if (this.dragHandle) { this.layer.destroyFeatures([this.dragHandle], {silent: true}); this.dragHandle = null; } if (this.radiusHandle) { this.layer.destroyFeatures([this.radiusHandle], {silent: true}); this.radiusHandle = null; } if (this.feature && this.feature.geometry && this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) { this.collectDragHandle(); } if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | OpenLayers.Control.ModifyFeature.RESIZE))) { this.collectRadiusHandle(); } if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) { if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) { this.collectVertices(); } } } this.collectRadiusHandle(); }, collectRadiusHandle: function () { var scope = this, feature = this.feature, data = feature.attributes, geometry = this.feature.geometry || this.backup_feature.geometry, center = this.feature.geometry.bounds.getCenterLonLat(); centroid = geometry.getCentroid().transform(this.WGS84_google_mercator, this.WGS84), lon = centroid.x, lat = centroid.y; if (data.type && Tms.settings.roadObjectTypeSettings[data.type].NoAzimuth) { return; } if (this.feature.geometry) { this.backup_feature = this.feature; } else { this.feature.geometry = this.backup_feature.geometry; } var originGeometry = new OpenLayers.Geometry.Point(lon, lat); var center_px = this.map.getPixelFromLonLat(center); // you can change this two values to get best radius geometry position. var pixel_dis_x = 20, pixel_dis_y = 20; var radius_px = center_px.add(pixel_dis_x, pixel_dis_y); var rotation_lonlat = this.map.getLonLatFromPixel(radius_px); var rotationFeatureGeometry = new OpenLayers.Geometry.Point( rotation_lonlat.lon, rotation_lonlat.lat ); var rotationFeature = new OpenLayers.Feature.Vector(rotationFeatureGeometry, null, this.rotateHandleStyle); var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE); var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE); var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE); rotationFeatureGeometry.move = function(x, y) { OpenLayers.Geometry.Point.prototype.move.call(this, x, y); var dx1 = this.x - originGeometry.x; var dy1 = this.y - originGeometry.y; var dx0 = dx1 - x; var dy0 = dy1 - y; if (rotate) { var a0 = Math.atan2(dy0, dx0); var a1 = Math.atan2(dy1, dx1); var angle = a1 - a0; angle *= 180 / Math.PI; var old_angle = feature.attributes.angle; var new_angle = old_angle - angle; feature.attributes.angle = new_angle; // redraw the feature scope.feature.layer.redraw.call(scope.feature.layer); } }; rotationFeature._sketch = true; this.radiusHandle = rotationFeature; this.radiusHandle.renderIntent = this.vertexRenderIntent; this.layer.addFeatures([this.radiusHandle], {silent: true}); }, CLASS_NAME: "OpenLayers.Control.RotateGraphicFeature" });