I'm using google maps in my application, and I have a webserver with a databse filled with lat/lon values. I want to mark them on the map, but I also want to cluster them together if they are within a certain pixel-distance of eachother.
I figure if I retrieve all my points from the database, I should be able to do something like this (pseudocode):
clusters[];
while(count(points)) {
cluster[];
point = points.pop();
boundingbox = pixelsToBB(point, pixeldistance, zoomlevel);
query = "select * from database where lat > boundingbox.minlat
and lat < boundingbox.maxlat and lng > boundingbox.minlng
and lng < boundingbox.maxlng";
for (result in executedquery) {
cluster[] += result;
points.remove(result);
}
clusters[] += cluster;
}
pixelsToBB(point, distance, zoomlevel) {
center = convertXY(point, zoomlevel);
maxlng = convertToLng(center.X, distance, zoomlevel);
minlng = convertToLng(center.X, -distance, zoomlevel);
minlat = convertToLat(center.Y, -distance, zoomlevel);
maxlat = convertToLat(center.Y, distance, zoomlevel);
return boundingbox(maxlng, maxlat, minlng, minlat);
}
What would my pixelsToBB function need to do with the zoomlevel? OR rather what would my convertToXY, convertToLng and convertToLat need to do? Am I thinking about this the right way, or are there any better ways to do it?
I'm not even sure what to search for, so if it's been asked before I'm sorry.
Using Google Maps API v3:
var latLng = // your position object here
var projection = map.getProjection();
var bounds = map.getBounds();
var topRight = projection.fromLatLngToPoint(bounds.getNorthEast());
var bottomLeft = projection.fromLatLngToPoint(bounds.getSouthWest());
var scale = Math.pow(2, map.getZoom());
var worldPoint = projection.fromLatLngToPoint(latLng);
return [Math.floor((worldPoint.x - bottomLeft.x) * scale), Math.floor((worldPoint.y - topRight.y) * scale)];
There is a JavaScript example to do this on this page as part of the documentation for the Google Maps API. Bear in mind you need to look at the page source to see it. It's not an actual documentation page but rather an example.
Related
Need some help with google maps polygon areas. I have a many markers plotted across the google map. Have some polygon areas plotted on map too. I want to find the total of marker points covered by a polygon, whenever the polygon area is clicked. Kindly guide or provide some good links in this direction
Thanks.
You could try ray casting algorithm. The implementation would be something like this:
var markers = []; // list of your markers
var polygonPath = polygon.getPath();
var location;
for (var i = 0; i < markers.length; i++) {
location = markers[i].getPosition();
console.log(isPositionInside(location.lat, location.lng, polygonPath));
}
function isPositionInside(mLat, mLng, polygonPoints) {
var isInside = false;
for (var a = 0, b = polygonPoints.length - 1; a < polygonPoints.length; b = a++) {
var aLng = polygonPoints[a].lng,
aLat = polygonPoints[a].lat,
bLng = polygonPoints[b].lng,
bLat = polygonPoints[b].lat;
if ((aLng > mLng) != (bLng > mLng) && (mLat < (bLat - aLat) * (mLng - aLng) / (bLng - aLng) + aLat)) {
isInside = !isInside;
}
}
return isInside;
};
This isn't the most optimal solution, as you can read in the wiki article, but in most cases it will get the job done.
I know how to find near by locations from MySQL database using Round circle radius query and I have given answer of the same on another SO question as well here.
But I wish to do some different thing from this now. Here what happened is the query returns the result from center of the point which includes entire circle of radius. I wish to get points only of the half circle. I know this is possible and its all mathematical calculation and I am little weak in it that's why asking for experts help.
See this image, it will give very clear idea.
As you can see in the image only front part location is needed, not the back side part. Need to ignore the back side part. Also I have divided the radius in different color to make them appear as zones - like red is zone1, orange is zone 2 and yellow is zone 3. This are virtual zones to filter the data (locations).
All suggestions are welcome.
You can plot points inside a segment using Haversine/Spherical Law of Cosines for the radius. Then use pointInPolygon() to find only those within segment. You will also require function to create polygon.
polySides = number of sides in polygon
pointLatArr = Lat of point in in polygon array
pointLngArr = Lng of point in in polygon array
dat.lat = Lat from Haversine results
dat.lng = Lng from Haversine results
if (pointInPolygonpolySides,pointLatArr,pointLngArr,dat.lat,dat.lng)){
var latlng = new google.maps.LatLng(dat.lat,dat.lng);
addMarker(latlng,dat.name);
bounds.extend(latlng);
}
function pointInPolygon(polySides,polyX,polyY,x,y) {
var j = polySides-1 ;
oddNodes = 0;
for (i=0; i<polySides; i++) {
if (polyY[i]<y && polyY[j]>=y || polyY[j]<y && polyY[i]>=y) {
if (polyX[i]+(y-polyY[i])/(polyY[j]-polyY[i])*(polyX[j]-polyX[i])<x) {
oddNodes=!oddNodes;
}
}
j=i; }
return oddNodes;
}
Function for segment polygon
function drawSegment(start,end,radius) {
var d2r = Math.PI / 180;
pointLatArr = new Array();
pointLngArr = new Array();
polyLatLngs = new Array(); // latLngs of polygon
var polyLat = (radius /3963.189) / d2r; // miles
var polyLng = polyLat / Math.cos(center.lat() * d2r);
var centerLatLng = new google.maps.LatLng(center.lat(),center.lng());//Center to start
pointLatArr.push(center.lat());
pointLngArr.push(center.lng());
polyLatLngs.push(centerLatLng);
bounds.extend(centerLatLng);
// Create polygon points (extra point to close polygon)
for (var i = start; i < end; i++) {
// Convert degrees to radians
var theta = i * d2r;
var pointLat = center.lat() + (polyLat * Math.sin(theta));
var pointLng = center.lng() + (polyLng * Math.cos(theta));
var pointLatLng = new google.maps.LatLng(
parseFloat(pointLat), parseFloat(pointLng));
polyLatLngs.push(pointLatLng);
pointLatArr.push(pointLat);
pointLngArr.push(pointLng);
bounds.extend(pointLatLng);
}
var centerLatLng = new google.maps.LatLng(center.lat(),center.lng());//End to center
polyLatLngs.push(centerLatLng);
pointLatArr.push(center.lat());
pointLngArr.push(center.lng());
polySides = polyLatLngs.length;
Map using this technique
}
See Demo
This is a portion of code I'm working on: (this legacy code is drawing Circle using Polygon paths:
GEvent.addListener(bigmap_rad, 'click', function(overlay, cpoint) {
var radius = document.getElementById('circle_radius').value;
var c_center = new GLatLng(cpoint.y,cpoint.x);
var c_marker = new GMarker(c_center);
var latOffset = 0.01;
var lonOffset = 0.01;
var latConv = c_center.distanceFrom(new GLatLng(c_center.lat()+0.1, c_center.lng()))/100;
var lngConv = c_center.distanceFrom(new GLatLng(c_center.lat(), c_center.lng()+0.1))/100;
// nodes = number of points to create polygon
var nodes = 40;
// Create an array of points
var cpoints = [];
var pointbegain = null;
// set the amount of steps from node
var step = parseInt(360/nodes);
// the for loop creates a series of points that define the circle, counting by the amount of steps, by 9 in the case of 40 nodes
for(var i=0; i<=360; i+=step){
var point1 = new GLatLng(c_center.lat() + (radius / latConv * Math.cos(i * Math.PI / 180)),
c_center.lng() + (radius / lngConv * Math.sin(i * Math.PI / 180)));
if(i==0){
pointbegain= point1;
}
cpoints.push(point1);
}
//cpoints.push(pointbegain);
polygon = new GPolygon(cpoints, "#000000", 1, 1, "#8000000", 0.5);
//bigmap_rad.addOverlay(polygon);
(Here bigmap_rad is a google map v2 Map object and cpoint is passed to that event listener)
I am using this google map v2 code to turn it into v3 . But stumbled on this
var c_center = new GLatLng(cpoint.y,cpoint.x);
I cant find the alternative of this cpoint.y and cpoint.x for google map api v3. Please someone suggest me the solution. Thanks in advance.
I think I got a solution. Just used this
var c_center = new google.maps.LatLng(event.latLng.lat(),event.latLng.lng());
in place of
var c_center = new GLatLng(cpoint.y,cpoint.x);
I had a similar issue and solved it by replacing the x and y with lat() and lng().
That would make your code be.
var c_center = new google.maps.LatLng(cpoint.lat(),cpoint.lng());
Maybe that could have worked and is a quick workaround that helped me.
ok so you need to have an event listener on the map for clicks. And you want to find out the point that's been clicked.
google.maps.event.addListener(map, 'click', function(event) {
console.log(event.latLng);
var yourCoordinates = event.latLng;
});
See https://developers.google.com/maps/documentation/javascript/events#EventArguments
I am trying to figure out a way to show items using the map script with the following info:
10 miles north running along US 1 plot a marker 10 feet to the right(east) of US 1. And to set for example that mile 0 starts at the intersection between US 1 and Main Street.
Perhaps someone has run into this before or something similar and will be kind enough to give me some pointers.
Thanks!
Following Eric's tip I was able to create a function to grab a polyline for the road centerline from Google Maps. Then use the google api service to convert that polyline into latitude and longitude coordinates. From then on, it was all spherical geometry coding. Below is my humble little code:
//Function to grab a centerline from the Map Api
//start = lat and long for beginning of roadway centerline ie [27.64681, -82.38438]
//end = lat and long for end of roadway centerline ie [27.71248, -82.33518]
//startmile = beginning milepost
function grabmap(start, end, startmile){
startmile = parseFloat(startmile);
var points = [];
var plinex = [];
var pliney = [];
var directions = Maps.newDirectionFinder().setOrigin(start).setDestination(end).getDirections();
// Much of this code is based on the template referenced in
// http://googleappsdeveloper.blogspot.com/2010/06/automatically-generate-maps-and.html
for (var i in directions.routes) {
for (var j in directions.routes[i].legs) {
for (var k in directions.routes[i].legs[j].steps) {
// Parse out the current step in the directions
var step = directions.routes[i].legs[j].steps[k];
// Call Maps.decodePolyline() to decode the polyline for
// this step into an array of latitudes and longitudes
var path = Maps.decodePolyline(step.polyline.points);
points = points.concat(path);
}
}
}
var lengthvector = (points.length / 2);
for ( i = 0; i <= points.length; i++){
if ( i % 2 == 0){
plinex = plinex.concat(points[i]);
pliney = pliney.concat(points[(i+1)]);
}
}
var plineVector = new Array(plinex.length - 2);
for ( i = 0; i <= plinex.length - 2; i++){
plineVector[i] = new Array(2);
plineVector[i][0] = plinex[i];
plineVector[i][1] = pliney[i];
}
return plineVector;
}
I haven't used the Maps Service extensively, but it should be possible. You use the DiretionFinder to get the latitudes and longitudes of points along the path, and then do some math to get the offset for the marker.
I have a google maps app which plots markers as it loads. One of the new requirment is to to add a Polygon overlay encompassing a selection of markers by the user. I was able to achieve that using the Geometry Controls of the GMaps Utility Library
Now, the next step is to form a group of the selected markers for which I would need to determine if the lat lngs of the markers falls within the lat lngs of the polygon? Is there a way to determine the lat lngs of a polygon and compute if the marker's lat lng is within its boundaries?
I have never directly messed around with Google Maps, but you can store the points that make up the polygon and then use the Point-In-polygon Algorithm to check if a given longitude and latitude point is within a polygon or not.
// Create polygon method for collision detection
GPolygon.prototype.containsLatLng = function(latLng) {
// Do simple calculation so we don't do more CPU-intensive calcs for obvious misses
var bounds = this.getBounds();
if(!bounds.containsLatLng(latLng)) {
return false;
}
// Point in polygon algorithm found at http://msdn.microsoft.com/en-us/library/cc451895.aspx
var numPoints = this.getVertexCount();
var inPoly = false;
var i;
var j = numPoints-1;
for(var i=0; i < numPoints; i++) {
var vertex1 = this.getVertex(i);
var vertex2 = this.getVertex(j);
if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) {
if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) {
inPoly = !inPoly;
}
}
j = i;
}
return inPoly;
};
Following npinti's suggestion, you may want to check out the following point-in-polygon implementation for Google Maps:
Check if a polygon contains a coordinate in Google Maps
This has been updated in v3. You can do this calculation via the google.maps.geometry.poly namespace API Documentation