I want to hide and show Google map circle. Hope the API has provided any simple method but I am unable to figure it out.
And can anyone let me know how to place the circle to left corner of the map?
Waiting for your help, here is my code:
/**
* A distance widget that will display a circle that can be resized and will
* provide the radius in km.
*
* #param {google.maps.Map} map The map to attach to.
*
* #constructor
*/
function DistanceWidget(map) {
this.set('map', map);
this.set('position', map.getCenter());
var marker = new google.maps.Marker({
draggable: true,
title: 'Move me!'
});
// Bind the marker map property to the DistanceWidget map property
marker.bindTo('map', this);
// Bind the marker position property to the DistanceWidget position
// property
marker.bindTo('position', this);
// Create a new radius widget
var radiusWidget = new RadiusWidget();
// Bind the radiusWidget map to the DistanceWidget map
radiusWidget.bindTo('map', this);
// Bind the radiusWidget center to the DistanceWidget position
radiusWidget.bindTo('center', this, 'position');
// Bind to the radiusWidgets' distance property
this.bindTo('distance', radiusWidget);
// Bind to the radiusWidgets' bounds property
this.bindTo('bounds', radiusWidget);
}
DistanceWidget.prototype = new google.maps.MVCObject();
/**
* A radius widget that add a circle to a map and centers on a marker.
*
* #constructor
*/
function RadiusWidget() {
var circle = new google.maps.Circle({
strokeWeight: 2
});
// Set the distance property value, default to 50km.
this.set('distance', 50);
// Bind the RadiusWidget bounds property to the circle bounds property.
this.bindTo('bounds', circle);
// Bind the circle center to the RadiusWidget center property
circle.bindTo('center', this);
// Bind the circle map to the RadiusWidget map
circle.bindTo('map', this);
// Bind the circle radius property to the RadiusWidget radius property
circle.bindTo('radius', this);
// Add the sizer marker
this.addSizer_();
}
RadiusWidget.prototype = new google.maps.MVCObject();
/**
* Update the radius when the distance has changed.
*/
RadiusWidget.prototype.distance_changed = function () {
this.set('radius', this.get('distance') * 1000);
};
/**
* Add the sizer marker to the map.
*
* #private
*/
RadiusWidget.prototype.addSizer_ = function () {
var sizer = new google.maps.Marker({
draggable: true,
title: 'Drag me!'
});
sizer.bindTo('map', this);
sizer.bindTo('position', this, 'sizer_position');
var me = this;
google.maps.event.addListener(sizer, 'drag', function () {
// Set the circle distance (radius)
me.setDistance();
});
};
/**
* Update the center of the circle and position the sizer back on the line.
*
* Position is bound to the DistanceWidget so this is expected to change when
* the position of the distance widget is changed.
*/
RadiusWidget.prototype.center_changed = function () {
var bounds = this.get('bounds');
// Bounds might not always be set so check that it exists first.
if (bounds) {
var lng = bounds.getNorthEast().lng();
// Put the sizer at center, right on the circle.
var position = new google.maps.LatLng(this.get('center').lat(), lng);
this.set('sizer_position', position);
}
};
/**
* Calculates the distance between two latlng points in km.
* #see http://www.movable-type.co.uk/scripts/latlong.html
*
* #param {google.maps.LatLng} p1 The first lat lng point.
* #param {google.maps.LatLng} p2 The second lat lng point.
* #return {number} The distance between the two points in km.
* #private
*/
RadiusWidget.prototype.distanceBetweenPoints_ = function (p1, p2) {
if (!p1 || !p2) {
return 0;
}
var R = 6371; // Radius of the Earth in km
var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * 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 = R * c;
return d;
};
/**
* Set the distance of the circle based on the position of the sizer.
*/
RadiusWidget.prototype.setDistance = function () {
// As the sizer is being dragged, its position changes. Because the
// RadiusWidget's sizer_position is bound to the sizer's position, it will
// change as well.
var pos = this.get('sizer_position');
var center = this.get('center');
var distance = this.distanceBetweenPoints_(center, pos);
// Set the distance property for any objects that are bound to it
this.set('distance', distance);
};
function init() {
var mapDiv = document.getElementById('map-canvas');
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(37.790234970864, -122.39031314844),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var distanceWidget = new DistanceWidget(map);
google.maps.event.addListener(distanceWidget, 'distance_changed', function () {
displayInfo(distanceWidget);
});
google.maps.event.addListener(distanceWidget, 'position_changed', function () {
displayInfo(distanceWidget);
});
}
function displayInfo(widget) {
var info = document.getElementById('info');
info.innerHTML = 'Position: ' + widget.get('position') + ', distance: ' + widget.get('distance');
}
google.maps.event.addDomListener(window, 'load', init);
Changed this code (made "map" and "distanceWidget" global):
var map = null;
var distanceWidget = null;
function init() {
var mapDiv = document.getElementById('map-canvas');
map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(37.790234970864, -122.39031314844),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
distanceWidget = new DistanceWidget(map);
google.maps.event.addListener(distanceWidget, 'distance_changed', function () {
displayInfo(distanceWidget);
});
google.maps.event.addListener(distanceWidget, 'position_changed', function () {
displayInfo(distanceWidget);
});
}
function displayInfo(widget) {
var info = document.getElementById('info');
info.innerHTML = 'Position: ' + widget.get('position') + ', distance: ' + widget.get('distance');
}
Added this to the HTML, hides and shows the distanceWidget:
<button onclick="distanceWidget.set('map',null);" >Hide</button>
<button onclick="distanceWidget.set('map',map);">Show</button>
<div id="info"></div>
<div id="map-canvas"></div>
working fiddle
Related
I want to make few groups and when I toggle specific I want to make it hidden. I will show you what I have made so far. But this hides everything, and I know why, because this marker.setVisible(true); the "marker" is global and it is not for specific group, but I don't know how to make it for specific group.
EDIT: Might not work, idk how to include js file...
Javascript
// SanMap.js
// Tool for drawing Google Maps of San Andreas.
// Written by Tim Potze
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org>
//
/* Create a set of helper classes.
*/
/**
* Projection specialized for San Andreas, based on GallPetersProjection
* available at:
* https://developers.google.com/maps/documentation/javascript/examples/map-projection-simple
* #class SanMapProjection
* #constructor
* *implements {google.maps.Projection}
*/
function SanMapProjection(tileSize) {
/**
* The range across the map.
*
* #property projectionRange_
* #type {Number}
*/
var projectionRange_ = tileSize;
/**
* The origin of the map.
*
* #property pixelOrigin_
* #type {Object}
*/
this.pixelOrigin_ = new google.maps.Point(projectionRange_ / 2,
projectionRange_ / 2);
/**
* The number of pixels per longitude degree.
*
* #property pixelsPerLonDegree_
* #type {Number}
*/
this.pixelsPerLonDegree_ = projectionRange_ / 360;
/**
* Converts a google.maps.LatLng to google.maps.Point.
*
* #method fromLatLngToPoint
* #param {Object} latLng The LatLng object to convert.
* #param {Object} opt_point optional point type to use as return type
* instead of google.maps.Point.
* #return {Object} The newly created point.
*/
this.fromLatLngToPoint = function (latLng, opt_point) {
var point = opt_point || new google.maps.Point(0, 0);
point.x = this.pixelOrigin_.x + latLng.lng() *
this.pixelsPerLonDegree_ * 2;
point.y = this.pixelOrigin_.y - latLng.lat() *
this.pixelsPerLonDegree_ * 2;
return point;
}
/**
* Converts a google.maps.Point to google.maps.LatLng.
*
* #method fromLatLngToPoint
* #param {Object} point The Point object to convert.
* #return {Object} The newly created LatLng.
*/
this.fromPointToLatLng = function (point) {
var lng = (point.x - this.pixelOrigin_.x) /
this.pixelsPerLonDegree_ / 2;
var lat = (-point.y + this.pixelOrigin_.y) /
this.pixelsPerLonDegree_ / 2;
return new google.maps.LatLng(lat, lng, true);
}
};
/**
* Simple class for providing a google.maps.ImageMapType based on the provided
* zoom limitations and function for providing tiles.
* #class SanMapType
* #constructor
*/
function SanMapType(minZoom, maxZoom, getTileUrl, tileSize) {
/**
* Creates an instance of google.maps.ImageMapType based on the provided
* zoom limitations and function for providing tiles.
*
* #method getImageMapType
* #param {Boolean} repeating Whether the map should repeat horizontally.
* #return {Object} The newly created ImageMapType.
*/
this.getImageMapType = function (repeating) {
/* Default tileSize to 512.
*/
tileSize = tileSize || 512;
return new google.maps.ImageMapType({
getTileUrl: function (coord, zoom) {
var x = coord.x,
y = coord.y,
max = 1 << zoom;
/* If not repeating and x is outside of the range -or- y is
* outside of the range, return a clear tile. This can be
* provided by getTileUrl, using the tile coordinates (-1, -1).
*/
if (y < 0 || y >= max ||
(repeating !== true && (x < 0 || x >= max))) {
return getTileUrl(zoom, -1, -1);
}
/*
* Return the provided tile. Make sure x is within the
* range 0 - max.
*/
return getTileUrl(zoom, (((x % max) + max) % max), y);
},
tileSize: new google.maps.Size(tileSize, tileSize),
maxZoom: maxZoom,
minZoom: minZoom
});
}
};
/* Define a number of SanMap methods.
*/
function SanMap(){ }
/**
* Creates an instance of google.maps.Map with the provided map types.
*
* #method createMap
* #param {Object} canvas The element to draw the map on.
* #param {Number} mapTypes The map types available in the map control.
* #param {Number} zoom The initial zoom level.
* #param {Object} center The intial center point to focus on.
* #param {Boolean} repeating Whether the map should repeat horizontally.
* #param {String} defaultMap The default map type.
* #return {Object} The newly created Map.
*/
SanMap.createMap = function(canvas, mapTypes, zoom, center, repeating,
defaultMap) {
/* If no mapTypes are parsed, return null and display a warning
*/
if (mapTypes === undefined || mapTypes.length == 0) {
console.warn("SanMap: No map types were parsed with SanMap.createMap.");
return null;
}
/* Create the map
*/
var map = new google.maps.Map(canvas, {
zoom: zoom || 2,
center: center || SanMap.getLatLngFromPos(90, -90),
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_TOP,
},
streetViewControl: false,
//mapTypeControl: false,
options: {
gestureHandling: 'greedy'
},
mapTypeControlOptions: {
mapTypeIds: Object.keys(mapTypes)
},
//restriction: {latLngBounds:{north: 90, south: -90, west: -90, east: 90}, strictBounds: false,}
});
/* Add every map type to the map.
*/
for (var key in mapTypes) {
if (mapTypes.hasOwnProperty(key)) {
var type = mapTypes[key].getImageMapType(repeating || false);
type.name = type.alt = key;
type.projection = new SanMapProjection(type.tileSize.width);
map.mapTypes.set(key, type);
}
}
/* Set the default map type.
*/
map.setMapTypeId(defaultMap || Object.keys(mapTypes)[0]);
/* If not repeating, bound the viewable area.
*/
if (!repeating) {
bounds = new google.maps.LatLngBounds(new google.maps.LatLng(-90,-90),
new google.maps.LatLng(90,90));
/* When the center changes, check if the new center is within the bounds
* of the map. If not, move the center to within these bounds.
*/
google.maps.event.addListener(map, 'center_changed', function () {
if (bounds.contains(map.getCenter()))
return;
var lng = map.getCenter().lng(),
lat = map.getCenter().lat();
if (lng < bounds.getSouthWest().lng())
lng = bounds.getSouthWest().lng();
if (lng > bounds.getNorthEast().lng())
lng = bounds.getNorthEast().lng();
if (lat < bounds.getSouthWest().lat())
lat = bounds.getSouthWest().lat();
if (lat > bounds.getNorthEast().lat())
lat = bounds.getNorthEast().lat();
map.setCenter(new google.maps.LatLng(lat, lng));
});
}
return map;
};
/* Conversion properties. */
SanMap.width = 6000;
SanMap.height = 6000;
SanMap.ox = 0;
SanMap.oy = 0;
/**
* Set the properties of the map coordinate system.
*
* #method setMapSize
* #param {Number} width The width of the map.
* #param {Number} y The GTA:SA y-coordinate.
*/
SanMap.setMapSize = function (width, height, offsetx, offsety) {
SanMap.width = width;
SanMap.height = height;
SanMap.ox = offsetx;
SanMap.oy = offsety;
}
/**
* Converts a GTA:SA coordinates to an instance of google.maps.LatLng.
*
* #method getLatLngFromPos
* #param {Number} x The GTA:SA x-coordinate.
* #param {Number} y The GTA:SA y-coordinate.
* #return {Object} The newly created LatLng.
*/
SanMap.getLatLngFromPos = function (x, y) {
return typeof(x) == "object"
? new google.maps.LatLng((x.y - SanMap.oy) / SanMap.height * 180, (x.x - SanMap.ox) / SanMap.width * 180)
: new google.maps.LatLng((y - SanMap.oy) / SanMap.height * 180, (x - SanMap.ox) / SanMap.width * 180);
}
/**
* Converts an instance of google.maps.LatLng to a GTA:SA coordinates.
*
* #method getPosFromLatLng
* #param {Object} latLng The LatLng to convert..
* #return {Object} An Object containing the GTA:SA coordinates.
*/
SanMap.getPosFromLatLng = function (latLng) {
return {x: latLng.lng() * SanMap.width / 180 + SanMap.ox, y: latLng.lat() * SanMap.height / 180 + SanMap.oy};
}
html, body, #map-canvas {
height: 100%;
padding: 0;
margin: 0;
}
<html>
<head>
<title>Old School - Mapa</title>
<!-- Disallow users to scale this page -->
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" href="style.css">
</head>
<body>
<input id="removeMarker" type="checkbox" checked="checked"></input> <!-- Toggle marker group 1 -->
<input id="removeMarker2" type="checkbox" checked="checked"></input> <!-- Toggle marker group 2 -->
<!-- The container the map is rendered in -->
<div id="map-canvas"></div>
<!-- Load all javascript -->
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://maps.google.com/maps/api/js?sensor=false&key=YOUR_API_KEY"></script>
<script src="SanMap.js"></script>
<script>
var mapType = new SanMapType(1, 3, function (zoom, x, y) {
return x == -1 && y == -1 ? "tiles/map.outer.png" : "tiles/map." + zoom + "." + x + "." + y + ".png";
});
var satType = new SanMapType(1, 3, function (zoom, x, y) {
return x == -1 && y == -1 ? "tiles/map.outer.png" : "tiles/sat." + zoom + "." + x + "." + y + ".png";
});
var map = SanMap.createMap(document.getElementById('map-canvas'),
{'Original': mapType, 'Satelit': satType}, 1, null, false, 'Original');
var locations = [ // Marker group 1
["Burg", 1215.7954,-923.9620, 'images/icon96.png']
];
var locations2 = [ // Marker group 2
["Casino", 2043.4570,1918.1044, 'images/icon89.png']
];
for (var i = 0; i < locations.length; i++) { // Marker group 1
placeMarker(locations[i]);
}
for (var i = 0; i < locations2.length; i++) { // Marker group 2
placeMarker(locations2[i]);
}
function placeMarker(loc) { // Get markers from all groups
var latLng = SanMap.getLatLngFromPos(loc[1], loc[2]);
var marker = new google.maps.Marker({
position: latLng,
map: map,
optimized: true,
icon: loc[3]
});
$('#removeMarker').click(function(event) { // Remove group 1 marker
if(this.checked)
{
$(':checkbox').each(function()
{
this.checked = true;
});
for (var i = 0; i < locations.length; i++) { marker.setVisible(true); }
}
else
{
$(':checkbox').each(function()
{
this.checked = false;
});
for(var i = 0; i < locations.length; i++) { marker.setVisible(false); }
}
});
$('#removeMarker2').click(function(event) { // Remove group 2 marker
if(this.checked)
{
$(':checkbox').each(function()
{
this.checked = true;
});
for (var i = 0; i < locations2.length; i++) { marker.setVisible(true); } // Here must be done something to be able to remove specific marker for that group
}
else
{
$(':checkbox').each(function()
{
this.checked = false;
});
for(var i = 0; i < locations2.length; i++) { marker.setVisible(false); } // Here must be done something to be able to remove specific marker for that group
}
});
var infowindow = new google.maps.InfoWindow();
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent("<div id='infowindow'>" + loc[0] + "</div>");
infowindow.open(map, marker);
});
map.addListener('click', function() { if(infowindow) infowindow.close(); });
}
</script>
</body>
The problem is that you call setVisible() method on the same marker each time when you loop in the locations array. to achieve what you want you need to store the markers objects when you create them, and after, when you need it, access it to delete them.
1 - Store markers object in array
First, create arrays corresponding to each group you want to create. After that, when you create markers, store them in the array corresponding of his category.
// Array for saving markers
let loc1Markers = []; // markers object from group 1
let loc2Markers = []; // markers object from group 2
// loop for creation of markers in each group
for (let i=0; i<locations.length; i++){
loc1Markers.push(placeMarker(locations[i])); // Marker pushed in group 1
}
for (var i = 0; i < locations2.length; i++) {
loc2Markers.push(placeMarker(locations2[i])); // Marker pushed in group 2
}
function placeMarker(loc){
// ... here the content of your function
return marker // return the marker object that you just created
}
In this exemple, the object marker created is returned at the end of the placeMarker function, and it is store directly in his corresponding array.
Loop through the marker array
Then, you can access each of your desired group by looping in your arrays of markers object. Modify all the lines like
for (var i = 0; i < locations.length; i++) { marker.setVisible(true); }
by calling directly the array of the group you want. Something like this for exemple :
for(let i=0; i<loc1Markers.length; i++){
loc1Markers[i].setVisible(true);
}
In this case you loop in the array with all of the markers of the group you want and when you call the setVisible() method, it's on different marker each time.
Does anyone know of an easy way to style the user controls (white dots on the bouns of the radius) for the editable circles in Google Maps? I cant find any way of doing this in the API documentation. At the very least it would be great to have the dots on a lower zindex than my pin but modify the zindex options of both has no results.
One option would be to do something like this example from the "article": Fun with MVC in the Google Maps Javascript API v3 documentation/ The handles there are just markers, the distance from the center is bound to the raduis, the center marker is bound to the center of the circle.
example fiddle
function init() {
var mapDiv = document.getElementById('map-canvas');
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(37.790234970864, -122.39031314844),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var distanceWidget = new DistanceWidget(map);
google.maps.event.addListener(distanceWidget, 'distance_changed', function() {
displayInfo(distanceWidget);
});
google.maps.event.addListener(distanceWidget, 'position_changed', function() {
displayInfo(distanceWidget);
});
}
google.maps.event.addDomListener(window, 'load', init);
Modified DistanceWidget code (changed the marker icons):
/**
* A distance widget that will display a circle that can be resized and will
* provide the radius in km.
*
* #param {google.maps.Map} map The map on which to attach the distance widget.
*
* #constructor
*/
function DistanceWidget(map) {
this.set('map', map);
this.set('position', map.getCenter());
var marker = new google.maps.Marker({
draggable: true,
icon: {
url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle_blue.png",
size: new google.maps.Size(7, 7),
anchor: new google.maps.Point(4, 4)
},
title: 'Move me!'
});
// Bind the marker map property to the DistanceWidget map property
marker.bindTo('map', this);
// Bind the marker position property to the DistanceWidget position
// property
marker.bindTo('position', this);
// Create a new radius widget
var radiusWidget = new RadiusWidget();
// Bind the radiusWidget map to the DistanceWidget map
radiusWidget.bindTo('map', this);
// Bind the radiusWidget center to the DistanceWidget position
radiusWidget.bindTo('center', this, 'position');
// Bind to the radiusWidgets' distance property
this.bindTo('distance', radiusWidget);
// Bind to the radiusWidgets' bounds property
this.bindTo('bounds', radiusWidget);
}
DistanceWidget.prototype = new google.maps.MVCObject();
/**
* A radius widget that add a circle to a map and centers on a marker.
*
* #constructor
*/
function RadiusWidget() {
var circle = new google.maps.Circle({
strokeWeight: 2
});
// Set the distance property value, default to 50km.
this.set('distance', 50);
// Bind the RadiusWidget bounds property to the circle bounds property.
this.bindTo('bounds', circle);
// Bind the circle center to the RadiusWidget center property
circle.bindTo('center', this);
// Bind the circle map to the RadiusWidget map
circle.bindTo('map', this);
// Bind the circle radius property to the RadiusWidget radius property
circle.bindTo('radius', this);
this.addSizer_();
}
RadiusWidget.prototype = new google.maps.MVCObject();
/**
* Update the radius when the distance has changed.
*/
RadiusWidget.prototype.distance_changed = function () {
this.set('radius', this.get('distance') * 1000);
};
/**
* Add the sizer marker to the map.
*
* #private
*/
RadiusWidget.prototype.addSizer_ = function () {
var sizer = new google.maps.Marker({
draggable: true,
icon: {
url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle_blue.png",
size: new google.maps.Size(7, 7),
anchor: new google.maps.Point(4, 4)
},
title: 'Drag me!'
});
sizer.bindTo('map', this);
sizer.bindTo('position', this, 'sizer_position');
var me = this;
google.maps.event.addListener(sizer, 'drag', function () {
// Set the circle distance (radius)
me.setDistance();
});
};
/**
* Update the center of the circle and position the sizer back on the line.
*
* Position is bound to the DistanceWidget so this is expected to change when
* the position of the distance widget is changed.
*/
RadiusWidget.prototype.center_changed = function () {
var bounds = this.get('bounds');
// Bounds might not always be set so check that it exists first.
if (bounds) {
var lng = bounds.getNorthEast().lng();
// Put the sizer at center, right on the circle.
var position = new google.maps.LatLng(this.get('center').lat(), lng);
this.set('sizer_position', position);
}
};
/**
* Calculates the distance between two latlng locations in km.
* #see http://www.movable-type.co.uk/scripts/latlong.html
*
* #param {google.maps.LatLng} p1 The first lat lng point.
* #param {google.maps.LatLng} p2 The second lat lng point.
* #return {number} The distance between the two points in km.
* #private
*/
RadiusWidget.prototype.distanceBetweenPoints_ = function (p1, p2) {
if (!p1 || !p2) {
return 0;
}
var R = 6371; // Radius of the Earth in km
var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * 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 = R * c;
return d;
};
/**
* Set the distance of the circle based on the position of the sizer.
*/
RadiusWidget.prototype.setDistance = function () {
// As the sizer is being dragged, its position changes. Because the
// RadiusWidget's sizer_position is bound to the sizer's position, it will
// change as well.
var pos = this.get('sizer_position');
var center = this.get('center');
var distance = this.distanceBetweenPoints_(center, pos);
// Set the distance property for any objects that are bound to it
this.set('distance', distance);
};
function displayInfo(widget) {
var info = document.getElementById('info');
info.innerHTML = 'Position: ' + widget.get('position').toUrlValue(3) + ', distance: ' + widget.get('distance').toFixed(3);
}
code snippet:
function init() {
var mapDiv = document.getElementById('map-canvas');
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(37.790234970864, -122.39031314844),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var distanceWidget = new DistanceWidget(map);
google.maps.event.addListener(distanceWidget, 'distance_changed', function() {
displayInfo(distanceWidget);
});
google.maps.event.addListener(distanceWidget, 'position_changed', function() {
displayInfo(distanceWidget);
});
}
google.maps.event.addDomListener(window, 'load', init);
/**
* A distance widget that will display a circle that can be resized and will
* provide the radius in km.
*
* #param {google.maps.Map} map The map on which to attach the distance widget.
*
* #constructor
*/
function DistanceWidget(map) {
this.set('map', map);
this.set('position', map.getCenter());
var marker = new google.maps.Marker({
draggable: true,
icon: {
url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle_blue.png",
size: new google.maps.Size(7, 7),
anchor: new google.maps.Point(4, 4)
},
title: 'Move me!'
});
// Bind the marker map property to the DistanceWidget map property
marker.bindTo('map', this);
// Bind the marker position property to the DistanceWidget position
// property
marker.bindTo('position', this);
// Create a new radius widget
var radiusWidget = new RadiusWidget();
// Bind the radiusWidget map to the DistanceWidget map
radiusWidget.bindTo('map', this);
// Bind the radiusWidget center to the DistanceWidget position
radiusWidget.bindTo('center', this, 'position');
// Bind to the radiusWidgets' distance property
this.bindTo('distance', radiusWidget);
// Bind to the radiusWidgets' bounds property
this.bindTo('bounds', radiusWidget);
}
DistanceWidget.prototype = new google.maps.MVCObject();
/**
* A radius widget that add a circle to a map and centers on a marker.
*
* #constructor
*/
function RadiusWidget() {
var circle = new google.maps.Circle({
strokeWeight: 2
});
// Set the distance property value, default to 50km.
this.set('distance', 50);
// Bind the RadiusWidget bounds property to the circle bounds property.
this.bindTo('bounds', circle);
// Bind the circle center to the RadiusWidget center property
circle.bindTo('center', this);
// Bind the circle map to the RadiusWidget map
circle.bindTo('map', this);
// Bind the circle radius property to the RadiusWidget radius property
circle.bindTo('radius', this);
this.addSizer_();
}
RadiusWidget.prototype = new google.maps.MVCObject();
/**
* Update the radius when the distance has changed.
*/
RadiusWidget.prototype.distance_changed = function() {
this.set('radius', this.get('distance') * 1000);
};
/**
* Add the sizer marker to the map.
*
* #private
*/
RadiusWidget.prototype.addSizer_ = function() {
var sizer = new google.maps.Marker({
draggable: true,
icon: {
url: "https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle_blue.png",
size: new google.maps.Size(7, 7),
anchor: new google.maps.Point(4, 4)
},
title: 'Drag me!'
});
sizer.bindTo('map', this);
sizer.bindTo('position', this, 'sizer_position');
var me = this;
google.maps.event.addListener(sizer, 'drag', function() {
// Set the circle distance (radius)
me.setDistance();
});
};
/**
* Update the center of the circle and position the sizer back on the line.
*
* Position is bound to the DistanceWidget so this is expected to change when
* the position of the distance widget is changed.
*/
RadiusWidget.prototype.center_changed = function() {
var bounds = this.get('bounds');
// Bounds might not always be set so check that it exists first.
if (bounds) {
var lng = bounds.getNorthEast().lng();
// Put the sizer at center, right on the circle.
var position = new google.maps.LatLng(this.get('center').lat(), lng);
this.set('sizer_position', position);
}
};
/**
* Calculates the distance between two latlng locations in km.
* #see http://www.movable-type.co.uk/scripts/latlong.html
*
* #param {google.maps.LatLng} p1 The first lat lng point.
* #param {google.maps.LatLng} p2 The second lat lng point.
* #return {number} The distance between the two points in km.
* #private
*/
RadiusWidget.prototype.distanceBetweenPoints_ = function(p1, p2) {
if (!p1 || !p2) {
return 0;
}
var R = 6371; // Radius of the Earth in km
var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * 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 = R * c;
return d;
};
/**
* Set the distance of the circle based on the position of the sizer.
*/
RadiusWidget.prototype.setDistance = function() {
// As the sizer is being dragged, its position changes. Because the
// RadiusWidget's sizer_position is bound to the sizer's position, it will
// change as well.
var pos = this.get('sizer_position');
var center = this.get('center');
var distance = this.distanceBetweenPoints_(center, pos);
// Set the distance property for any objects that are bound to it
this.set('distance', distance);
};
function displayInfo(widget) {
var info = document.getElementById('info');
info.innerHTML = 'Position: ' + widget.get('position').toUrlValue(3) + ', distance: ' + widget.get('distance').toFixed(3);
}
html,
body,
#map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?v=3&libraries=geometry"></script>
<div id="info"></div>
<div id="map-canvas"></div>
I'm afraid you can't. The API provides no control over those handles, and since the circles live inside a canvas, there's no easy way to identify the element in order to style it.
Edit:
You can declare your circle as non-editable and allow edits only on mouseover.
var circle=new google.maps.Circle({
center: map.getCenter(),
map: map,
editable: false
});
google.maps.event.addListener(circle, 'mouseover', function () {
circle.set('editable',true);
});
google.maps.event.addListener(circle, 'mouseout', function () {
circle.set('editable',false);
});
One simple way of adding style to the edit pointer is as follows.
#map div[style^="position: absolute; left: 0px; top: 0px; width: 9px; height: 9px;"] {
border-color: #777575 !important;
border-width: 2px !important;
}
I'm using google maps addon of Advanced Custom Fields for WordPress. I have multiple points on the map, some are very close to one another. I can open open them no problem, however I need to have one open at time. So if I open one, it closes the others. The Marker set up on click for advanced custom fields is as follows:
if( $marker.html() )
{
// create info window
var infowindow = new google.maps.InfoWindow({
content : $marker.html(),
maxWidth: 250
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.open( map, marker );
});
}
Any help on how to append this so only one window is open at a time would be greatly appreciated
<script type="text/javascript">
(function($) {
/*
* render_map
*
* This function will render a Google Map onto the selected jQuery element
*
* #type function
* #date 8/11/2013
* #since 4.3.0
*
* #param $el (jQuery element)
* #return n/a
*/
function render_map( $el ) {
// var
var $markers = $el.find('.marker');
// vars
var args = {
zoom : 16,
center : new google.maps.LatLng(0, 0),
mapTypeId : google.maps.MapTypeId.ROADMAP
};
// create map
var map = new google.maps.Map( $el[0], args);
// add a markers reference
map.markers = [];
// add markers
$markers.each(function(){
add_marker( $(this), map );
});
// center map
center_map( map );
}
// create info window outside of each - then tell that singular infowindow to swap content based on click
var infowindow = new google.maps.InfoWindow({
content : ''
});
/*
* add_marker
*
* This function will add a marker to the selected Google Map
*
* #type function
* #date 8/11/2013
* #since 4.3.0
*
* #param $marker (jQuery element)
* #param map (Google Map object)
* #return n/a
*/
function add_marker( $marker, map ) {
// var
var latlng = new google.maps.LatLng( $marker.attr('data-lat'), $marker.attr('data-lng') );
// create marker
var marker = new google.maps.Marker({
position : latlng,
map : map
});
// add to array
map.markers.push( marker );
// if marker contains HTML, add it to an infoWindow
if( $marker.html() )
{
// show info window when marker is clicked & close other markers
google.maps.event.addListener(marker, 'click', function() {
//swap content of that singular infowindow
infowindow.setContent($marker.html());
infowindow.open(map, marker);
});
// close info window when map is clicked
google.maps.event.addListener(map, 'click', function(event) {
if (infowindow) {
infowindow.close(); }
});
}
}
/*
* center_map
*
* This function will center the map, showing all markers attached to this map
*
* #type function
* #date 8/11/2013
* #since 4.3.0
*
* #param map (Google Map object)
* #return n/a
*/
function center_map( map ) {
// vars
var bounds = new google.maps.LatLngBounds();
// loop through all markers and create bounds
$.each( map.markers, function( i, marker ){
var latlng = new google.maps.LatLng( marker.position.lat(), marker.position.lng() );
bounds.extend( latlng );
});
// only 1 marker?
if( map.markers.length == 1 )
{
// set center of map
map.setCenter( bounds.getCenter() );
map.setZoom( 16 );
}
else
{
// fit to bounds
map.fitBounds( bounds );
}
}
/*
* document ready
*
* This function will render each map when the document is ready (page has loaded)
*
* #type function
* #date 8/11/2013
* #since 5.0.0
*
* #param n/a
* #return n/a
*/
$(document).ready(function(){
$('.acf-map').each(function(){
render_map( $(this) );
});
});
})(jQuery);
</script>
I have a array of point with latitude and longitude. Next, I add all points to my map.
I need solution/algorithm to move user to the nearest point from my array using geoloation on page load.
(if geolocation success of course)
This should do the trick. I combined both HTML5 geolocation to find the user's current location and Haversine function to calculate distances from a set of locations and the user's current location. The set of locations is defined in the JS array 'locations'.
<!DOCTYPE html>
<html>
<head>
<title>Google Map Template with Marker at User's Position</title>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script> <!-- Google Maps API -->
<script>
// set of locations represented by lat/lon pairs
var locations = [{lat:45, lon:-120}, {lat:44, lon:-121}, {lat:45.6, lon:-120.5}];
var map; // Google map object
// Initialize and display a google map
function Init()
{
// HTML5/W3C Geolocation
if ( navigator.geolocation )
{
navigator.geolocation.getCurrentPosition( UserLocation, errorCallback,{maximumAge:60000,timeout:10000});
}
// Default to Washington, DC
else
ClosestLocation( 38.8951, -77.0367, "Washington, DC" );
}
function errorCallback( error )
{
}
// Callback function for asynchronous call to HTML5 geolocation
function UserLocation( position )
{
ClosestLocation( position.coords.latitude, position.coords.longitude, "This is my Location" );
}
// Display a map centered at the specified coordinate with a marker and InfoWindow.
function ClosestLocation( lat, lon, title )
{
// Create a Google coordinate object for where to center the map
var latlng = new google.maps.LatLng( lat, lon );
// Map options for how to display the Google map
var mapOptions = { zoom: 12, center: latlng };
// Show the Google map in the div with the attribute id 'map-canvas'.
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
// Place a Google Marker at the same location as the map center
// When you hover over the marker, it will display the title
var marker = new google.maps.Marker( {
position: latlng,
map: map,
title: title
});
// Create an InfoWindow for the marker
var contentString = "<b>" + title + "</b>"; // HTML text to display in the InfoWindow
var infowindow = new google.maps.InfoWindow( { content: contentString } );
// Set event to display the InfoWindow anchored to the marker when the marker is clicked.
google.maps.event.addListener( marker, 'click', function() { infowindow.open( map, marker ); });
// find the closest location to the user's location
var closest = 0;
var mindist = 99999;
for(var i = 0; i < locations.length; i++)
{
// get the distance between user's location and this point
var dist = Haversine( locations[ i ].lat, locations[ i ].lon, lat, lon );
// check if this is the shortest distance so far
if ( dist < mindist )
{
closest = i;
mindist = dist;
}
}
// Create a Google coordinate object for the closest location
var latlng = new google.maps.LatLng( locations[ closest].lat, locations[ closest].lon );
// Place a Google Marker at the closest location as the map center
// When you hover over the marker, it will display the title
var marker2 = new google.maps.Marker( {
position: latlng,
map: map,
title: "Closest Location to User: Distance is " + mindist + " km"
});
// Create an InfoWindow for the marker
var contentString = "<b>" + "Closest Location to User: Distance is " + mindist + " km" + "</b>"; // HTML text to display in the InfoWindow
var infowindow = new google.maps.InfoWindow( { content: contentString } );
// Set event to display the InfoWindow anchored to the marker when the marker is clicked.
google.maps.event.addListener( marker2, 'click', function() { infowindow.open( map, marker2 ); });
map.setCenter( latlng );
}
// Call the method 'Init()' to display the google map when the web page is displayed ( load event )
google.maps.event.addDomListener( window, 'load', Init );
</script>
<script>
// Convert Degress to Radians
function Deg2Rad( deg ) {
return deg * Math.PI / 180;
}
// Get Distance between two lat/lng points using the Haversine function
// First published by Roger Sinnott in Sky & Telescope magazine in 1984 (“Virtues of the Haversine”)
//
function Haversine( lat1, lon1, lat2, lon2 )
{
var R = 6372.8; // Earth Radius in Kilometers
var dLat = Deg2Rad(lat2-lat1);
var dLon = Deg2Rad(lon2-lon1);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Deg2Rad(lat1)) * Math.cos(Deg2Rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
// Return Distance in Kilometers
return d;
}
// Get Distance between two lat/lng points using the Pythagoras Theorem on a Equirectangular projection to account
// for curvature of the longitude lines.
function PythagorasEquirectangular( lat1, lon1, lat2, lon2 )
{
lat1 = Deg2Rad(lat1);
lat2 = Deg2Rad(lat2);
lon1 = Deg2Rad(lon1);
lon2 = Deg2Rad(lon2);
var R = 6371; // km
var x = (lon2-lon1) * Math.cos((lat1+lat2)/2);
var y = (lat2-lat1);
var d = Math.sqrt(x*x + y*y) * R;
return d;
}
</script>
<style>
/* style settings for Google map */
#map-canvas
{
width : 500px; /* map width */
height: 500px; /* map height */
}
</style>
</head>
<body>
<!-- Dislay Google map here -->
<div id='map-canvas' ></div>
</body>
</html>
your help would be much appreciated with the following. I am using the JS code below to display a Google Map with a re-sizable circle overlay to output a centre point co-ordinate, radius and bounding box:
function DistanceWidget(map) {
this.set('map', map);
this.set('position', map.getCenter());
var marker = new google.maps.Marker({
draggable: true,
title: 'Drag to set centre',
icon: 'images/mapicon3.png'
});
marker.bindTo('map', this);
marker.bindTo('position', this);
var radiusWidget = new RadiusWidget();
radiusWidget.bindTo('map', this);
radiusWidget.bindTo('center', this, 'position');
this.bindTo('distance', radiusWidget);
this.bindTo('bounds', radiusWidget);
}
DistanceWidget.prototype = new google.maps.MVCObject();
function RadiusWidget() {
var circle = new google.maps.Circle({
fillColor: '#efefef',
fillOpacity: 0.5,
strokeColor: '#000',
strokeOpacity: 1.0,
strokeWeight: 2
});
this.set('distance', 5);
this.bindTo('bounds', circle);
circle.bindTo('center', this);
circle.bindTo('map', this);
circle.bindTo('radius', this);
this.addSizer_();
}
RadiusWidget.prototype = new google.maps.MVCObject();
RadiusWidget.prototype.distance_changed = function() {
this.set('radius', this.get('distance') * 1000);
};
RadiusWidget.prototype.addSizer_ = function() {
var sizer = new google.maps.Marker({
draggable: true,
title: 'Drag to expand search area',
icon: 'images/mapicon2.png'
});
sizer.bindTo('map', this);
sizer.bindTo('position', this, 'sizer_position');
var me = this;
google.maps.event.addListener(sizer, 'drag', function() {
me.setDistance();
});
};
RadiusWidget.prototype.center_changed = function() {
var bounds = this.get('bounds');
if (bounds) {
var lng = bounds.getNorthEast().lng();
var position = new google.maps.LatLng(this.get('center').lat(), lng);
this.set('sizer_position', position);
}
};
RadiusWidget.prototype.distanceBetweenPoints_ = function(p1, p2) {
if (!p1 || !p2) {
return 0;
}
var R = 6371;
var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * 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 = R * c;
return d;
};
RadiusWidget.prototype.setDistance = function() {
var pos = this.get('sizer_position');
var center = this.get('center');
var distance = this.distanceBetweenPoints_(center, pos);
var distance = Math.round(distance*100)/100
this.set('distance', distance);
};
function init() {
var mapDiv = document.getElementById('map-canvas');
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(geoip_latitude(), geoip_longitude()), zoom: 11,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var distanceWidget = new DistanceWidget(map);
google.maps.event.addListener(distanceWidget, 'distance_changed', function() {
displayInfo(distanceWidget);
});
google.maps.event.addListener(distanceWidget, 'position_changed', function() {
displayInfo(distanceWidget);
});
mapDiv.style.width = (viewportwidth)+"px";
mapDiv.style.height = (viewportheight)+"px";
}
function displayInfo(widget) {
var info = document.getElementById('info');
info.innerHTML = '<form action="/search" method="post"><input type="hidden" name="position" value="' + widget.get('position') + '" /><input type="hidden" name="distance" value="' + widget.get('distance') + '" /><input type="hidden" name="bounds" value="' + widget.get('bounds') + '" /><input type="submit" value="Submit" /></form>';
}
google.maps.event.addDomListener(window, 'load', init);
This works great, but what I can't figure out is how to add geocoding to this, so that a place name could be entered (POSTed via a form), geocoded using Google Maps API and set as the centre point in the above script, without breaking the current functionality.
As requested, there is a jsFiddle for the above here. You will see that the user can drag the markers to output position, distance and bounds; however what I want to add is the ability to enter a location in the form, which on submit is geocoded, with the resulting co-ordinates being used to reposition the centre marker.
Any help much appreciated, thanks.
Here is the JSFiddle Demo: and type in zipcode (30084) to test it:
Here is the initial markup of the HTML:
<div id="map-canvas"></div>
<div id="info">
</div>
<div id='geocode'>
<input name="q" type="text" id="q" /><br />
<input type="submit" value="Submit" id="geosubmit" />
</div>
Here is how you can get the Geocode information based on the input, and within the callback function of your geocode you can set the center of your DistanceWidget. The responseResult would give you lat by calling responses[0].geometry.location.lat() and lng by responses[0].geometry.location.lng():
function init() {
var mapDiv = document.getElementById('map-canvas');
var geocoder = new google.maps.Geocoder();
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(51.5001524, -0.1262362),
zoom: 11,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var distanceWidget = new DistanceWidget(map);
//Geocoder input section and logic
var mySubmit = document.getElementById('geosubmit');
var myGeoInfo = document.getElementById('q');
mySubmit.onclick = function() {
geocoder.geocode({
address: myGeoInfo.value
}, function(responses) {
if (responses && responses.length > 0) {
var newMarkerPos = new google.maps.LatLng(responses[0].geometry.location.lat(), responses[0].geometry.location.lng());
distanceWidget.set('position', newMarkerPos); //sets the new position of marker
distanceWidget.map.setCenter(newMarkerPos); //sets map's center
} else {
//response failed output error message
alert('error getting geocode');
}
});
}
google.maps.event.addListener(distanceWidget, 'distance_changed', function() {
displayInfo(distanceWidget);
});
google.maps.event.addListener(distanceWidget, 'position_changed', function() {
displayInfo(distanceWidget);
});
mapDiv.style.width = "500px";
mapDiv.style.height = "300px";
}
Update. Please check the JSFiddle demo and type in zipcode (30084) to test it:
To set your current RadiusWidget and google map marker here is the modified code. You don't need to modify the prototype of your current widget to complicate things. You can just call the MVCObject's set option and access the map by using the instantiated distancewidget:
geocoder.geocode({
address: myGeoInfo.value
}, function(responses) {
if (responses && responses.length > 0) {
var newMarkerPos = new google.maps.LatLng(responses[0].geometry.location.lat(), responses[0].geometry.location.lng());
distanceWidget.set('position', newMarkerPos); //sets the new position of marker
distanceWidget.map.setCenter(newMarkerPos); //sets map's center
} else {
//response failed output error message
alert('error getting geocode');
}
});
If you change your marker in DistanceWidget to be a instance variable instead, and create a setPosition() method operating on that marker I think you should be able to do what you want;
function DistanceWidget(map) {
this.set('map', map);
this.set('position', map.getCenter());
this.marker = new google.maps.Marker({
draggable: true,
title: 'Drag to set centre',
icon: 'images/mapicon3.png'
});
this.marker.bindTo('map', this);
this.marker.bindTo('position', this);
var radiusWidget = new RadiusWidget();
radiusWidget.bindTo('map', this);
radiusWidget.bindTo('center', this, 'position');
this.bindTo('distance', radiusWidget);
this.bindTo('bounds', radiusWidget);
}
DistanceWidget.prototype = new google.maps.MVCObject();
/* Add the `setMarkerPosition` method to this class */
DistanceWidget.prototype.setMarkerPosition = function(position) {
this.marker.setPosition(position);
}
And your init()-function becomes;
function init() {
var mapDiv = document.getElementById('map-canvas');
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(geoip_latitude(), geoip_longitude()), zoom: 11,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var distanceWidget = new DistanceWidget(map);
/* Set up geo-functionality */
var geo = new google.maps.Geocoder();
geo.geocode({address: 'Piccadilly Circus, London'}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
distanceWidget.setMarkerPosition(results[0].geometry.location);
map.setCenter(results[0].geometry.location);
}
});
...
DISCLAIMER: This is just from the top of my head, not tested plus I've never worked with these classes.
There is a tutorial here that shows how to get geocoding and reverse geocoding working with Google Maps v3 and Jquery