Draw ring (not circle) in Google Maps API - google-maps

I would like to draw a ring 20km thick with empty 5km circle inside. I dont how how to do it. I believe it is possible.
One simple solution could be to substract one 5km circle from 25km circle. Is it possible? Thank you for any tips.

Create a drawCircle function:
function drawCircle(point, radius, dir) {
var d2r = Math.PI / 180; // degrees to radians
var r2d = 180 / Math.PI; // radians to degrees
var earthsradius = 3963; // 3963 is the radius of the earth in miles
var points = 32;
// find the raidus in lat/lon
var rlat = (radius / earthsradius) * r2d;
var rlng = rlat / Math.cos(point.lat() * d2r);
var extp = new Array();
if (dir==1) {var start=0;var end=points+1} // one extra here makes sure we connect the
else {var start=points+1;var end=0}
for (var i=start; (dir==1 ? i < end : i > end); i=i+dir)
{
var theta = Math.PI * (i / (points/2));
ey = point.lng() + (rlng * Math.cos(theta)); // center a + radius x * cos(theta)
ex = point.lat() + (rlat * Math.sin(theta)); // center b + radius y * sin(theta)
extp.push(new google.maps.LatLng(ex, ey));
bounds.extend(extp[extp.length-1]);
}
// alert(extp.length);
return extp;
}
Then you can use it like this:
var donut = new google.maps.Polygon({
paths: [drawCircle(new google.maps.LatLng(-33.9,151.2), 100, 1),
drawCircle(new google.maps.LatLng(-33.9,151.2), 50, -1)],
strokeColor: "#0000FF",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
});
donut.setMap(map);
Note that the inner circle needs to "wind" opposite the outer circle.
Example (as posted by Dr Molle)
code snippet:
function drawCircle(point, radius, dir) {
var d2r = Math.PI / 180; // degrees to radians
var r2d = 180 / Math.PI; // radians to degrees
var earthsradius = 3963; // 3963 is the radius of the earth in miles
var points = 32;
// find the raidus in lat/lon
var rlat = (radius / earthsradius) * r2d;
var rlng = rlat / Math.cos(point.lat() * d2r);
var extp = new Array();
if (dir == 1) {
var start = 0;
var end = points + 1
} // one extra here makes sure we connect the
else {
var start = points + 1;
var end = 0
}
for (var i = start;
(dir == 1 ? i < end : i > end); i = i + dir) {
var theta = Math.PI * (i / (points / 2));
ey = point.lng() + (rlng * Math.cos(theta)); // center a + radius x * cos(theta)
ex = point.lat() + (rlat * Math.sin(theta)); // center b + radius y * sin(theta)
extp.push(new google.maps.LatLng(ex, ey));
bounds.extend(extp[extp.length - 1]);
}
// alert(extp.length);
return extp;
}
var map = null;
var bounds = null;
function initialize() {
var myOptions = {
zoom: 10,
center: new google.maps.LatLng(-33.9, 151.2),
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
},
navigationControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
bounds = new google.maps.LatLngBounds();
var donut = new google.maps.Polygon({
paths: [drawCircle(new google.maps.LatLng(-33.9, 151.2), 100, 1),
drawCircle(new google.maps.LatLng(-33.9, 151.2), 50, -1)
],
strokeColor: "#0000FF",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
});
donut.setMap(map);
map.fitBounds(bounds);
}
google.maps.event.addDomListener(window, "load", initialize);
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map_canvas"></div>

Here's a slightly modified version of geocodezip's answer using the geometry library:
function getCirclePoints(center, radius, numPoints, clockwise) {
var points = [];
for (var i = 0; i < numPoints; ++i) {
var angle = i * 360 / numPoints;
if (!clockwise) {
angle = 360 - angle;
}
// the maps API provides geometrical computations
// just make sure you load the required library (libraries=geometry)
var p = google.maps.geometry.spherical.computeOffset(center, radius, angle);
points.push(p);
}
// 'close' the polygon
points.push(points[0]);
return points;
}
You can use it like this:
new google.maps.Polygon({
paths: [
getCirclePoints(yourCenter, outerRadius, numPoints, true),
getCirclePoints(yourCenter, innerRadius, numPoints, false)
],
/* other polygon options */
});
Edit:
The geometry API can be added in Node like this (thanks to Gil Epshtain):
require('google-maps-api')(GOOGLE_API_KEY, ['geometry'])
By the time I was writing this, I used plain old JavaScript inclusion in HTML:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry">
For more information, please refer to the API page of the geometry library.

Ok, so based on geocodezip's answer, I adapted the function to draw a donut on Google Maps that caters for the distortion of the map. The function calculates 360 Lat/Lon points of the circle, based on a center point and the radius. The Lat/Lon is calculated as the bearing (0 to 360 degree/points) and distance from the center.
function drawCircle(point, radius, dir) {
var olat = point[0] * Math.PI / 180;
var olon = point[1] * Math.PI / 180;
var ERadius = 6371; // Radius of the earth
var points = 360; // Calculate number of points
var circle = new Array();
if (dir == 1) {
var start = 0;
var end = points + 1;
} else {
var start = points + 1;
var end = 0;
}
for (var i = start; (dir==1 ? i < end : i > end); i = i + dir) {
var Bearing = i * Math.PI / 180;
var cLat = Math.asin(Math.sin(olat)*Math.cos(radius/ERadius)+Math.cos(olat)*Math.sin(radius/ERadius)*Math.cos(Bearing)) * 180 / Math.PI;
var cLon = (olon + Math.atan2(Math.sin(Bearing)*Math.sin(radius/ERadius)*Math.cos(olat), Math.cos(radius/ERadius)-Math.sin(olat)*Math.sin(Math.asin(Math.sin(olat)*Math.cos(radius/ERadius)+Math.cos(olat)*Math.sin(radius/ERadius)*Math.cos(Bearing))))) * 180 / Math.PI;
circle.push(new google.maps.LatLng(cLat, cLon));
bounds.extend(circle[circle.length-1]);
}
return circle;
}
Based on that, you can use the following function to draw the donut.
var donut = new google.maps.Polygon({ paths: [drawCircle([22.3089, 113.9150], 5000, 1), drawCircle([22.3089, 113.9150], 9000, -1)], strokeColor: "#AA0000",strokeWeight: 0,fillColor: "#AA0000",fillOpacity: 0.25 });
donut.setMap(map);
The example above is centered in Hong Kong and has in inner circle with a radius of 5,000km, and an outer circle with a radius of 9,000km. This should produce a donut like this:

For anyone who landed here while trying to draw a donut on Google My Maps, for me the following worked:
Draw the outer polygon with the "Add line or shape" tool
Draw the inner polygon with the "Add line or shape" tool (just put it on top of the first one, don't care about the hole yet)
On the layer, click the three dots menu, then "Export data" > "CSV". Save the file on your computer
Open the CSV file you just exported, and edit it with a text editor, so that it has the format:
WKT,name,description
"POLYGON ((Outer_polygon_coordinates),(inner_polygon_coordinates))",Polygon Name,
Save the csv in the format above.
Import the csv back into google my maps. It now has the correct format :)

Related

Make a marker face the correct direction of movement as it moves on the map [duplicate]

I'd like to know if it possible to change the marker orientation according the path drawn on the map. Here is a example:
As you can see the marker is a car (with front bumper and tail lights). I'd like to orientate the car in the direction the path is going (in this exemple orientate the car around 45 degrees to the right).
I'm using DirectionsService to draw the path and I have a random number of point. Sometime only one, sometime 10 points. I'm adding the markers before drawing the paths. Here is how I'm drawing the path:
// Intialize the Path Array
var path = new google.maps.MVCArray();
// Intialise the Direction Service
var service = new google.maps.DirectionsService();
// Set the Path Stroke Color
var poly = new google.maps.Polyline({ map: gmap, strokeColor: '#dd0000' }); // #4986E7
// Draw the path for this vehicle
for (var i = 0; i < pathPoints.length; i++) {
if ((i + 1) < pathPoints.length) {
var src = pathPoints[i];
var des = pathPoints[i + 1];
path.push(src);
poly.setPath(path);
service.route({
origin: src,
destination: des,
travelMode: google.maps.DirectionsTravelMode.DRIVING
}, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
for (var i = 0, len = result.routes[0].overview_path.length; i < len; i++){
path.push(result.routes[0].overview_path[i]);
}
}
});
}
}
Is it possible to do that?
One option is to translate your icon to SVG then use the Symbol rotation property to align it with the road (you can do the same with a PNG image by either making a large number of copies of the icon rotated by a degree or two, or by making a custom icon that allows you to rotate the PNG icon arbitrarily)
marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn,p);
icon.rotation = heading;
marker.setIcon(icon);
proof of concept fiddle
code snippet:
var map;
var directionDisplay;
var directionsService;
var stepDisplay;
var markerArray = [];
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005,
wait = 1;
var infowindow = null;
var timerHandle = null;
function createMarker(latlng, label, html) {
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
function initialize() {
infowindow = new google.maps.InfoWindow({
size: new google.maps.Size(150, 50)
});
// Instantiate a directions service.
directionsService = new google.maps.DirectionsService();
// Create a map and center it on Manhattan.
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
address = 'new york';
geocoder = new google.maps.Geocoder();
geocoder.geocode({
'address': address
}, function(results, status) {
map.setCenter(results[0].geometry.location);
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
// Instantiate an info window to hold step text.
stepDisplay = new google.maps.InfoWindow();
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
}
var steps = [];
function calcRoute() {
if (timerHandle) {
clearTimeout(timerHandle);
}
if (marker) {
marker.setMap(null);
}
polyline.setMap(null);
poly2.setMap(null);
directionsDisplay.setMap(null);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
};
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING;
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
// Route the directions and pass the response to a
// function to create markers for each step.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
startLocation = new Object();
endLocation = new Object();
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i === 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
// marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
for (k = 0; k < nextSegment.length; k++) {
polyline.getPath().push(nextSegment[k]);
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
map.fitBounds(bounds);
map.setZoom(18);
startAnimation();
}
});
}
var step = 50; // 5; // metres
var tick = 100; // milliseconds
var eol;
var k = 0;
var stepnum = 0;
var speed = "";
var lastVertex = 1;
//=============== animation functions ======================
function updatePoly(d) {
// Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
if (poly2.getPath().getLength() > 20) {
poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
// map.addOverlay(poly2)
}
if (polyline.GetIndexAtDistance(d) < lastVertex + 2) {
if (poly2.getPath().getLength() > 1) {
poly2.getPath().removeAt(poly2.getPath().getLength() - 1);
}
poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
} else {
poly2.getPath().insertAt(poly2.getPath().getLength(), endLocation.latlng);
}
}
function animate(d) {
if (d > eol) {
map.panTo(endLocation.latlng);
marker.setPosition(endLocation.latlng);
return;
}
var p = polyline.GetPointAtDistance(d);
map.panTo(p);
var lastPosn = marker.getPosition();
marker.setPosition(p);
var heading = google.maps.geometry.spherical.computeHeading(lastPosn, p);
icon.rotation = heading;
marker.setIcon(icon);
updatePoly(d);
timerHandle = setTimeout("animate(" + (d + step) + ")", tick);
}
function startAnimation() {
eol = polyline.Distance();
map.setCenter(polyline.getPath().getAt(0));
marker = new google.maps.Marker({
position: polyline.getPath().getAt(0),
map: map,
icon: icon
});
poly2 = new google.maps.Polyline({
path: [polyline.getPath().getAt(0)],
strokeColor: "#0000FF",
strokeWeight: 10
});
// map.addOverlay(poly2);
setTimeout("animate(50)", 2000); // Allow time for the initial map display
}
google.maps.event.addDomListener(window, 'load', initialize);
//=============== ~animation funcitons =====================
var car = "M17.402,0H5.643C2.526,0,0,3.467,0,6.584v34.804c0,3.116,2.526,5.644,5.643,5.644h11.759c3.116,0,5.644-2.527,5.644-5.644 V6.584C23.044,3.467,20.518,0,17.402,0z M22.057,14.188v11.665l-2.729,0.351v-4.806L22.057,14.188z M20.625,10.773 c-1.016,3.9-2.219,8.51-2.219,8.51H4.638l-2.222-8.51C2.417,10.773,11.3,7.755,20.625,10.773z M3.748,21.713v4.492l-2.73-0.349 V14.502L3.748,21.713z M1.018,37.938V27.579l2.73,0.343v8.196L1.018,37.938z M2.575,40.882l2.218-3.336h13.771l2.219,3.336H2.575z M19.328,35.805v-7.872l2.729-0.355v10.048L19.328,35.805z";
var icon = {
path: car,
scale: .7,
strokeColor: 'white',
strokeWeight: .10,
fillOpacity: 1,
fillColor: '#404040',
offset: '5%',
// rotation: parseInt(heading[i]),
anchor: new google.maps.Point(10, 25) // orig 10,50 back of car, 10,0 front of car, 10,25 center of car
};
/*********************************************************************\
* *
* epolys.js by Mike Williams *
* updated to API v3 by Larry Ross *
* *
* A Google Maps API Extension *
* *
* Adds various Methods to google.maps.Polygon and google.maps.Polyline *
* *
* .Contains(latlng) returns true is the poly contains the specified *
* GLatLng *
* *
* .Area() returns the approximate area of a poly that is *
* not self-intersecting *
* *
* .Distance() returns the length of the poly path *
* *
* .Bounds() returns a GLatLngBounds that bounds the poly *
* *
* .GetPointAtDistance() returns a GLatLng at the specified distance *
* along the path. *
* The distance is specified in metres *
* Reurns null if the path is shorter than that *
* *
* .GetPointsAtDistance() returns an array of GLatLngs at the *
* specified interval along the path. *
* The distance is specified in metres *
* *
* .GetIndexAtDistance() returns the vertex number at the specified *
* distance along the path. *
* The distance is specified in metres *
* Returns null if the path is shorter than that *
* *
* .Bearing(v1?,v2?) returns the bearing between two vertices *
* if v1 is null, returns bearing from first to last *
* if v2 is null, returns bearing from v1 to next *
* *
* *
***********************************************************************
* *
* This Javascript is provided by Mike Williams *
* Blackpool Community Church Javascript Team *
* http://www.blackpoolchurch.org/ *
* http://econym.org.uk/gmap/ *
* *
* This work is licenced under a Creative Commons Licence *
* http://creativecommons.org/licenses/by/2.0/uk/ *
* *
***********************************************************************
* *
* Version 1.1 6-Jun-2007 *
* Version 1.2 1-Jul-2007 - fix: Bounds was omitting vertex zero *
* add: Bearing *
* Version 1.3 28-Nov-2008 add: GetPointsAtDistance() *
* Version 1.4 12-Jan-2009 fix: GetPointsAtDistance() *
* Version 3.0 11-Aug-2010 update to v3 *
* *
\*********************************************************************/
// === first support methods that don't (yet) exist in v3
google.maps.LatLng.prototype.distanceFrom = function(newLatLng) {
var EarthRadiusMeters = 6378137.0; // meters
var lat1 = this.lat();
var lon1 = this.lng();
var lat2 = newLatLng.lat();
var lon2 = newLatLng.lng();
var dLat = (lat2 - lat1) * Math.PI / 180;
var dLon = (lon2 - lon1) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = EarthRadiusMeters * c;
return d;
}
google.maps.LatLng.prototype.latRadians = function() {
return this.lat() * Math.PI / 180;
}
google.maps.LatLng.prototype.lngRadians = function() {
return this.lng() * Math.PI / 180;
}
// === A method which returns the length of a path in metres ===
google.maps.Polygon.prototype.Distance = function() {
var dist = 0;
for (var i = 1; i < this.getPath().getLength(); i++) {
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
return dist;
}
// === A method which returns a GLatLng of a point a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetPointAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
if (this.getPath().getLength() < 2) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
var p1 = this.getPath().getAt(i - 2);
var p2 = this.getPath().getAt(i - 1);
var m = (metres - olddist) / (dist - olddist);
return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);
}
// === A method which returns an array of GLatLngs of points a given interval along the path ===
google.maps.Polygon.prototype.GetPointsAtDistance = function(metres) {
var next = metres;
var points = [];
// some awkward special cases
if (metres <= 0) return points;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength()); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
while (dist > next) {
var p1 = this.getPath().getAt(i - 1);
var p2 = this.getPath().getAt(i);
var m = (next - olddist) / (dist - olddist);
points.push(new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m));
next += metres;
}
}
return points;
}
// === A method which returns the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polygon.prototype.GetIndexAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
return i;
}
// === Copy all the above functions to GPolyline ===
google.maps.Polyline.prototype.Distance = google.maps.Polygon.prototype.Distance;
google.maps.Polyline.prototype.GetPointAtDistance = google.maps.Polygon.prototype.GetPointAtDistance;
google.maps.Polyline.prototype.GetPointsAtDistance = google.maps.Polygon.prototype.GetPointsAtDistance;
google.maps.Polyline.prototype.GetIndexAtDistance = google.maps.Polygon.prototype.GetIndexAtDistance;
html {
height: 100%;
}
body {
height: 100%;
margin: 0px;
font-family: Helvetica, Arial;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="tools">start:
<input type="text" name="start" id="start" value="union square, NY" />end:
<input type="text" name="end" id="end" value="times square, NY" />
<input type="submit" onclick="calcRoute();" />
</div>
<div id="map_canvas" style="width:100%;height:90%;"></div>
Easiest solution for this (FOR GOOGLE MAPS) :
var rotationAngle = google.maps.geometry.spherical.computeHeading(oldLatlng, currentlatlng);
$('img[src="path_to_marker_icon_image"]').css({
transform: "rotate(" + rotationAngle + "deg)"
});
You can use it easily do with the help of the 'GMSMapView' Delegates method.
var lastMapBearing: CLLocationDirection?
var lastUserBearing: CLLocationDirection?
func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
lastMapBearing = position.bearing
driverMarker?.rotation = (lastUserBearing ?? 0) - lastMapBearing!
}
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
lastUserBearing = newHeading.trueHeading
driverMarker?.rotation = lastUserBearing! - (lastMapBearing ?? 0)
}
Make sure you can assign the delegate itSelf.

Draw Hexagon based on zip code in google map?

I want to draw Hexagon polygon on google map based on zip code (single lat, long). How can I get other lat long so I can pass it to polygon array based on single lat long, It would be also based on any radius e.g. 15 miles or 15 km.
Here is sample google map image.
One option is to use code from Mike Williams' eshapes ported to v3.
proof of concept fiddle
from his documentation for a RegularPoly:
Plots a regular polygon (e.g. pentagon, square, octagon) with a specified centre, radius, number of vertices, and rotation.
Parameters
latlng Centre of the shape
radius Length of radius in metres
vertexCount Number of vertices, e.g. 5 for a pentagon
rotation Degrees of rotation.
When zero or omitted the southern edge of the shape is horizontal
color Color parameter for polyline
weight Weight parameter for polyline
opacity Opacity parameter for polyline
opts Options parameter for polyline
code snippet:
var geocoder = new google.maps.Geocoder();
var map;
function initialize() {
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
codeAddress("Atlanta, GA");
}
function codeAddress(address) {
geocoder.geocode({
'address': address
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
// === Hexagon ===
var point = results[0].geometry.location;
var hex1 = google.maps.Polygon.RegularPoly(point, 25000, 6, 60, "#000000", 1, 1, "#ff0000", 0.5);
hex1.setMap(map);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
google.maps.event.addDomListener(window, "load", initialize);
// original code from Mike Williams' eshapes.js
// http://econym.org.uk/gmap/eshapes.htm
// ported to the Google Maps Javascript API v3
google.maps.Polygon.RegularPoly = function(point, radius, vertexCount, rotation, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts) {
rotation = rotation || 0;
var tilt = !(vertexCount & 1);
return google.maps.Polygon.Shape(point, radius, radius, radius, radius, rotation, vertexCount, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts, tilt)
}
google.maps.Polygon.Shape = function(point, r1, r2, r3, r4, rotation, vertexCount, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts, tilt) {
var rot = -rotation * Math.PI / 180;
var points = [];
var latConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat() + 0.1, point.lng())) * 10;
var lngConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat(), point.lng() + 0.1)) * 10;
var step = (360 / vertexCount) || 10;
var flop = -1;
if (tilt) {
var I1 = 180 / vertexCount;
} else {
var I1 = 0;
}
for (var i = I1; i <= 360.001 + I1; i += step) {
var r1a = flop ? r1 : r3;
var r2a = flop ? r2 : r4;
flop = -1 - flop;
var y = r1a * Math.cos(i * Math.PI / 180);
var x = r2a * Math.sin(i * Math.PI / 180);
var lng = (x * Math.cos(rot) - y * Math.sin(rot)) / lngConv;
var lat = (y * Math.cos(rot) + x * Math.sin(rot)) / latConv;
points.push(new google.maps.LatLng(point.lat() + lat, point.lng() + lng));
}
return (new google.maps.Polygon({
paths: points,
strokeColor: strokeColour,
strokeWeight: strokeWeight,
strokeOpacity: Strokepacity,
fillColor: fillColour,
fillOpacity: fillOpacity
}))
}
function EOffset(point, easting, northing) {
var latConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat() + 0.1, point.lng())) * 10;
var lngConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat(), point.lng() + 0.1)) * 10;
return new google.maps.LatLng(point.lat() + northing / latConv, point.lng() + easting / lngConv)
}
function EOffsetBearing(point, dist, bearing) {
var latConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat() + 0.1, point.lng())) * 10;
var lngConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat(), point.lng() + 0.1)) * 10;
var lat = dist * Math.cos(bearing * Math.PI / 180) / latConv;
var lng = dist * Math.sin(bearing * Math.PI / 180) / lngConv;
return new google.maps.LatLng(point.lat() + lat, point.lng() + lng)
}
html,
body,
#map_canvas {
height: 500px;
width: 500px;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map_canvas" style="border: 2px solid #3872ac;"></div>
You could calculate and draw hexagon area based on lat/lng using the following approach:
function drawHexagon(map,position,radius){
var coordinates = [];
for(var angle= -90;angle < 270; angle+=60) {
coordinates.push(google.maps.geometry.spherical.computeOffset(position, radius, angle));
}
// Construct the polygon.
var polygon = new google.maps.Polygon({
paths: coordinates,
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35
});
polygon.setMap(map);
map.setCenter(position);
}
Complete example
function initialize() {
var mapOptions = {
zoom: 8,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(document.getElementById('map-canvas'),mapOptions);
var position = new google.maps.LatLng(33.748589, -84.390392); //Atlanta, GA, USA
var radius = 50 * 1000; //radius in meters
drawHexagon(map,position,radius);
}
function drawHexagon(map,position,radius){
var coordinates = [];
for(var angle= -90;angle < 270; angle+=60) {
coordinates.push(google.maps.geometry.spherical.computeOffset(position, radius, angle));
}
// Construct the polygon.
var polygon = new google.maps.Polygon({
paths: coordinates,
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35
});
polygon.setMap(map);
map.setCenter(position);
}
google.maps.event.addDomListener(window, 'load', initialize);
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px;
}
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true&libraries=geometry"></script>
<div id="map-canvas"></div>

Draw a rectangle google.maps.Polygon given its center point and dimensions

I am working on a PHP script which takes XML input, parses it and then displays (eventually rotated) rectangle and ellipses areas.
Because area can be rotated, I have to use google.maps.Polygon and not Rectangle.
To handle rotation I hope to use the google-maps-polygon-rotate library (that part comes later).
My problem is: from the given XML input I only know the coordinates of the rectangle centerpoint and the dimensions of the area (width and height).
Currently I just display the centerpoint as a marker:
My question is: how to draw a rectangle with google.maps.Polygon when only the latitude and longitude of the center point and the width, height are known?
I.e. how to calculate the latitude and longitude of the 4 endpoints?
Can I somehow use google.maps.geometry.spherical.computeOffset() method here?
One option would be to use the v3 ported version of Mike Williams' v2 Eshapes library
// ==- Tilted rectangles ===
var point = new google.maps.LatLng(44, -78);
var tiltedRectangle1 = google.maps.Polygon.Shape(point, 50000, 10000, 50000, 10000, -60, 4, "#000000", 3, 1, "#ffffff", 1, {}, true);
var tiltedRectangle2 = google.maps.Polyline.Shape(point, 50000, 10000, 50000, 10000, 30, 4, "#000000", 3, 1, {}, true);
tiltedRectangle1.setMap(map);
tiltedRectangle2.setMap(map);
The function google.maps.Polygon.Shape(point, 50000, 10000, 50000, 10000, -60, defines a rectangle with sides 100000 meters x 20000 meters rotated -60 degrees, the second call defines one the same size rotated 30 degrees.
working fiddle
working snippet:
var map = null;
function initialize() {
var myOptions = {
zoom: 8,
center: new google.maps.LatLng(44, -78),
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
},
navigationControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map"),
myOptions);
// ==- Tilted rectangles ===
var point = new google.maps.LatLng(44, -78);
var tiltedRectangle1 = google.maps.Polygon.Shape(point, 50000, 10000, 50000, 10000, -60, 4, "#000000", 3, 1, "#ffffff", 1, {}, true);
var tiltedRectangle2 = google.maps.Polyline.Shape(point, 50000, 10000, 50000, 10000, 30, 4, "#000000", 3, 1, {}, true);
tiltedRectangle1.setMap(map);
tiltedRectangle2.setMap(map);
}
google.maps.event.addDomListener(window, 'load', initialize);
// EShapes.js
//
// Based on an idea, and some lines of code, by "thetoy"
//
// This Javascript is provided by Mike Williams
// Community Church Javascript Team
// http://www.bisphamchurch.org.uk/
// http://econym.org.uk/gmap/
//
// This work is licenced under a Creative Commons Licence
// http://creativecommons.org/licenses/by/2.0/uk/
//
// Version 0.0 04/Apr/2008 Not quite finished yet
// Version 1.0 10/Apr/2008 Initial release
// Version 3.0 12/Oct/2011 Ported to v3 by Lawrence Ross
google.maps.Polyline.Shape = function (point, r1, r2, r3, r4, rotation, vertexCount, colour, weight, opacity, opts, tilt) {
if (!colour) {
colour = "#0000FF";
}
if (!weight) {
weight = 4;
}
if (!opacity) {
opacity = 0.45;
}
var rot = -rotation * Math.PI / 180;
var points = [];
var latConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat() + 0.1, point.lng())) * 10;
var lngConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat(), point.lng() + 0.1)) * 10;
var step = (360 / vertexCount) || 10;
var flop = -1;
if (tilt) {
var I1 = 180 / vertexCount;
} else {
var I1 = 0;
}
for (var i = I1; i <= 360.001 + I1; i += step) {
var r1a = flop ? r1 : r3;
var r2a = flop ? r2 : r4;
flop = -1 - flop;
var y = r1a * Math.cos(i * Math.PI / 180);
var x = r2a * Math.sin(i * Math.PI / 180);
var lng = (x * Math.cos(rot) - y * Math.sin(rot)) / lngConv;
var lat = (y * Math.cos(rot) + x * Math.sin(rot)) / latConv;
points.push(new google.maps.LatLng(point.lat() + lat, point.lng() + lng));
}
return (new google.maps.Polyline({
path: points,
strokeColor: colour,
strokeWeight: weight,
strokeOpacity: opacity
}))
}
google.maps.Polygon.Shape = function (point, r1, r2, r3, r4, rotation, vertexCount, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts, tilt) {
var rot = -rotation * Math.PI / 180;
var points = [];
var latConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat() + 0.1, point.lng())) * 10;
var lngConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat(), point.lng() + 0.1)) * 10;
var step = (360 / vertexCount) || 10;
var flop = -1;
if (tilt) {
var I1 = 180 / vertexCount;
} else {
var I1 = 0;
}
for (var i = I1; i <= 360.001 + I1; i += step) {
var r1a = flop ? r1 : r3;
var r2a = flop ? r2 : r4;
flop = -1 - flop;
var y = r1a * Math.cos(i * Math.PI / 180);
var x = r2a * Math.sin(i * Math.PI / 180);
var lng = (x * Math.cos(rot) - y * Math.sin(rot)) / lngConv;
var lat = (y * Math.cos(rot) + x * Math.sin(rot)) / latConv;
points.push(new google.maps.LatLng(point.lat() + lat, point.lng() + lng));
}
return (new google.maps.Polygon({
paths: points,
strokeColor: strokeColour,
strokeWeight: strokeWeight,
strokeOpacity: Strokepacity,
fillColor: fillColour,
fillOpacity: fillOpacity
}))
}
html, body, #map {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk""></script>
<div id="map"></div>
My own answer (see the screenshot below) - first add the geometry library:
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry">
</script>
And then use it to create the corners of the rectangle:
var NORTH = 0;
var WEST = -90;
var SOUTH = 180;
var EAST = 90;
function drawRect(map, lat, lng, width, height, color) {
var center = new google.maps.LatLng(lat, lng);
var north = google.maps.geometry.spherical.computeOffset(center, height / 2, NORTH);
var south = google.maps.geometry.spherical.computeOffset(center, height / 2, SOUTH);
var northEast = google.maps.geometry.spherical.computeOffset(north, width / 2, EAST);
var northWest = google.maps.geometry.spherical.computeOffset(north, width / 2, WEST);
var southEast = google.maps.geometry.spherical.computeOffset(south, width / 2, EAST);
var southWest = google.maps.geometry.spherical.computeOffset(south, width / 2, WEST);
var corners = [ northEast, northWest, southWest, southEast ];
var rect = new google.maps.Polygon({
paths: corners,
strokeColor: color,
strokeOpacity: 0.9,
strokeWeight: 1,
fillColor: color,
fillOpacity: 0.3,
map: map
});
}
And to rotate the rectangle by an angle I probably could add it to the 2nd argument of the computeOffset() calls. Haven't tried that yet.

Google maps - draw ellipse based off 4 coordinates

I would like to draw an ellipse on google maps based off four coordinates, like the current "rectangle" method available via the API:
var rectangle = new google.maps.Rectangle({
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: map,
bounds: new google.maps.LatLngBounds(
new google.maps.LatLng(33.671068, -116.25128),
new google.maps.LatLng(33.685282, -116.233942))
});
(using the bounds paramater).
If that fails, is there an easy way to convert the distance between 2 polygons to a unit of measurement?
You have to calculate the path yourself. This should help:
http://mathworld.wolfram.com/Ellipse.html
Edit: This might be more useful:
http://www.geocodezip.com/v3_MW_example_eshapes.html
A v3 port of Mike Williams' v2 eshapes library, supports ellipse (but not based on the bounds).
Working example that sizes to the map bounds.
proof of concept fiddle
code snippet:
var map = null;
var myOptions = {
zoom: 8,
center: new google.maps.LatLng(43, -79.5),
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
},
navigationControl: true,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map"),
myOptions);
google.maps.event.addListenerOnce(map, "bounds_changed", function() {
var bounds = map.getBounds();
var major_axis = google.maps.geometry.spherical.computeDistanceBetween(bounds.getNorthEast(), new google.maps.LatLng(bounds.getSouthWest().lat(), bounds.getNorthEast().lng())) / 2;
var minor_axis = google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(bounds.getCenter().lat(), bounds.getSouthWest().lng()),
new google.maps.LatLng(bounds.getCenter().lat(), bounds.getNorthEast().lng())) / 2;
// === Ellipse ===
var point = map.getCenter(); // new google.maps.LatLng(43,-78);
var ellipse = google.maps.Polygon.Ellipse(point, major_axis, minor_axis, 0, "#000000", 2, 1, "#ffff00", 0.5);
ellipse.setMap(map);
});
// This Javascript is based on code provided by the
// Community Church Javascript Team
// http://www.bisphamchurch.org.uk/
// http://econym.org.uk/gmap/
// EShapes.js
//
// Based on an idea, and some lines of code, by "thetoy"
//
// This Javascript is provided by Mike Williams
// Community Church Javascript Team
// http://www.bisphamchurch.org.uk/
// http://econym.org.uk/gmap/
//
// This work is licenced under a Creative Commons Licence
// http://creativecommons.org/licenses/by/2.0/uk/
//
// Version 0.0 04/Apr/2008 Not quite finished yet
// Version 1.0 10/Apr/2008 Initial release
// Version 3.0 12/Oct/2011 Ported to v3 by Lawrence Ross
google.maps.Polygon.Ellipse = function(point, r1, r2, rotation, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts) {
rotation = rotation || 0;
return google.maps.Polygon.Shape(point, r1, r2, r1, r2, rotation, 100, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts)
}
google.maps.Polygon.Shape = function(point, r1, r2, r3, r4, rotation, vertexCount, strokeColour, strokeWeight, Strokepacity, fillColour, fillOpacity, opts, tilt) {
var rot = -rotation * Math.PI / 180;
var points = [];
var latConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat() + 0.1, point.lng())) * 10;
var lngConv = google.maps.geometry.spherical.computeDistanceBetween(point, new google.maps.LatLng(point.lat(), point.lng() + 0.1)) * 10;
var step = (360 / vertexCount) || 10;
var flop = -1;
if (tilt) {
var I1 = 180 / vertexCount;
} else {
var I1 = 0;
}
for (var i = I1; i <= 360.001 + I1; i += step) {
var r1a = flop ? r1 : r3;
var r2a = flop ? r2 : r4;
flop = -1 - flop;
var y = r1a * Math.cos(i * Math.PI / 180);
var x = r2a * Math.sin(i * Math.PI / 180);
var lng = (x * Math.cos(rot) - y * Math.sin(rot)) / lngConv;
var lat = (y * Math.cos(rot) + x * Math.sin(rot)) / latConv;
points.push(new google.maps.LatLng(point.lat() + lat, point.lng() + lng));
}
return (new google.maps.Polygon({
paths: points,
strokeColor: strokeColour,
strokeWeight: strokeWeight,
strokeOpacity: Strokepacity,
fillColor: fillColour,
fillOpacity: fillOpacity
}))
}
html,
body,
#map {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map"></div>
Not sure if this is what you're looking for, but here's a sample I made(click two points anywhere), it uses a function that takes two latLngs and returns a series of points that describe the ellipse, then adds those to a polygon.
Note that it assumes that the bounding box is relatively small (and away from the poles) to take the points as coplanar.

GMaps: Cover all earth in polygon and make a hole in that

I'm drawing two polygons on the map. The second polygon makes a hole in the first one. I want the first polygon to cover as much as possible of the earth. So lets focus on that and drop the hole for the time.
Since max/min lat = 90,-90 and max/min lng = 180, -180.
If I draw the following the seems to "eat each other up"
nw = new google.maps.LatLng(90, -180, true);
ne = new google.maps.LatLng(90, 180, true);
se = new google.maps.LatLng(-90, 180, true);
sw = new google.maps.LatLng(-90, -180, true);
points = [nw, ne, se, sw];
If I tweak the values a little I can get them to not eat up each other but I'm always left with quite a big miss.
Thanks in advance.
I got it to work, here is the code:
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false">
</script>
<script type="text/javascript">
function drawCircle(point, radius, dir) {
var d2r = Math.PI / 180; // degrees to radians
var r2d = 180 / Math.PI; // radians to degrees
var earthsradius = 3963; // 3963 is the radius of the earth in miles
var points = 1000;
// find the raidus in lat/lon
var rlat = (radius / earthsradius) * r2d;
var rlng = rlat / Math.cos(point.lat() * d2r);
var extp = new Array();
if (dir==1) {var start=0;var end=points+1} // one extra here makes sure we connect the
else {var start=points+1;var end=0}
for (var i=start; (dir==1 ? i < end : i > end); i=i+dir)
{
var theta = Math.PI * (i / (points/2));
ey = point.lng() + (rlng * Math.cos(theta)); // center a + radius x * cos(theta)
ex = point.lat() + (rlat * Math.sin(theta)); // center b + radius y * sin(theta)
extp.push(new google.maps.LatLng(ex, ey));
bounds.extend(extp[extp.length-1]);
}
// alert(extp.length);
return extp;
}
var map = null;
var bounds = null;
function initialize() {
var myOptions = {
zoom: 10,
center: new google.maps.LatLng(42,-80),
mapTypeControl: true,
mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU},
navigationControl: true,
mapTypeId: google.maps.MapTypeId.TERRAIN
}
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
bounds = new google.maps.LatLngBounds();
var donut = new google.maps.Polygon({
paths: [triangleCoords = [
new google.maps.LatLng(-87, 120),
new google.maps.LatLng(-87, -87),
new google.maps.LatLng(-87, 0)],
drawCircle(new google.maps.LatLng(42,-80), 1000, -1)],
strokeColor: "#000000",
strokeOpacity: 0.6,
strokeWeight: 2,
fillColor: "#999999",
fillOpacity: 0.6
});
donut.setMap(map);
map.fitBounds(bounds);
}
</script>