latLng from polyLine Percentage - google-maps-api-3

How do I return a latLng value from a given percentage along a polyLine?
I have spent a day on this using interpolate and individual nodes. Is their an easier function out there that does the grunt work?
Google maps API v3
thanks!

http://www.geocodezip.com/scripts/v3_epoly.js
written for the Google Maps Javascript API v2 ported to v3. Documentation for the v2 version
Has these two methods:
.Distance() returns the length of the poly path
.GetPointAtDistance() returns a GLatLng at the specified distance
along the path.
The distance is specified in metres
Returns null if the path is shorter than that
This should work (if you include that script and your polyline variable is "polyline"):
var latlng = polyline.GetPointAtDistance(polyline.Distance()*(desired percentage)/100);
of course if you polyline isn't changing in length, it would be more efficient to compute the length once an use it each time you want to find a point on the polyline.
var polylength = polyline.Distance();
var latlng = polylength*(desired percentage)/100);
live example
code snippet:
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
var polyline = null;
var marker;
var infowindow;
function createMarker(latlng, label, html) {
// alert("createMarker("+latlng+","+label+","+html+","+color+")");
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,
contentString: contentString
});
marker.myname = label;
// gmarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(this.contentString);
infowindow.open(map, marker);
});
return marker;
}
var myLatLng = null;
var lat;
var lng;
var zoom = 2;
var maptype;
function initialize() {
infowindow = new google.maps.InfoWindow();
myLatLng = new google.maps.LatLng(37.422104808, -122.0838851);
maptype = google.maps.MapTypeId.ROADMAP;
// If there are any parameters at eh end of the URL, they will be in location.search
// looking something like "?marker=3"
// skip the first character, we are not interested in the "?"
var query = location.search.substring(1);
// split the rest at each "&" character to give a list of "argname=value" pairs
var pairs = query.split("&");
for (var i = 0; i < pairs.length; i++) {
// break each pair at the first "=" to obtain the argname and value
var pos = pairs[i].indexOf("=");
var argname = pairs[i].substring(0, pos).toLowerCase();
var value = pairs[i].substring(pos + 1);
// process each possible argname - use unescape() if theres any chance of spaces
if (argname == "filename") {
filename = unescape(value);
}
if (argname == "lat") {
lat = parseFloat(value);
}
if (argname == "lng") {
lng = parseFloat(value);
}
if (argname == "start") {
document.getElementById("start").value = decodeURI(value);
}
if (argname == "end") {
document.getElementById("end").value = decodeURI(value);
}
if (argname == "time") {
document.getElementById("time").value = decodeURI(value);
// putMarkerOnRoute(parseFloat(document.getElementById('time').value));
}
if (argname == "zoom") {
zoom = parseInt(value);
}
if (argname == "type") {
// from the v3 documentation 8/24/2010
// HYBRID This map type displays a transparent layer of major streets on satellite images.
// ROADMAP This map type displays a normal street map.
// SATELLITE This map type displays satellite images.
// TERRAIN This map type displays maps with physical features such as terrain and vegetation.
if (value == "m") {
maptype = google.maps.MapTypeId.ROADMAP;
}
if (value == "k") {
maptype = google.maps.MapTypeId.SATELLITE;
}
if (value == "h") {
maptype = google.maps.MapTypeId.HYBRID;
}
if (value == "t") {
maptype = google.maps.MapTypeId.TERRAIN;
}
}
}
if (!isNaN(lat) && !isNaN(lng)) {
myLatLng = new google.maps.LatLng(lat, lng);
}
var myOptions = {
zoom: zoom,
center: myLatLng,
mapTypeId: maptype
};
directionsDisplay = new google.maps.DirectionsRenderer({
suppressMarkers: true
});
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
directionsDisplay.setMap(map);
calcRoute();
}
function calcRoute() {
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
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
polyline.setPath([]);
var bounds = new google.maps.LatLngBounds();
startLocation = new Object();
endLocation = new Object();
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById("directions_panel");
summaryPanel.innerHTML = "";
// 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 = google.maps.Marker({map:map,position: startLocation.latlng});
// 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);
computeTotalDistance(response);
putMarkerOnRoute(parseFloat(document.getElementById('percent').value));
} else {
alert("directions response " + status);
}
});
}
var totalDist = 0;
var totalTime = 0;
function computeTotalDistance(result) {
totalDist = 0;
totalTime = 0;
var myroute = result.routes[0];
for (i = 0; i < myroute.legs.length; i++) {
totalDist += myroute.legs[i].distance.value;
totalTime += myroute.legs[i].duration.value;
}
totalDist = totalDist / 1000.
document.getElementById("total").innerHTML = "total distance is: " + totalDist + " km<br>total time is: " + (totalTime / 60).toFixed(2) + " minutes<br>average speed is: " + (totalDist / (totalTime / 3600)).toFixed(2) + " kph";
document.getElementById("totalTime").value = (totalTime / 60.).toFixed(2);
}
function putMarkerOnRoute(percent) {
if (percent > 100) {
percent = 100;
document.getElementById('percent').value = percent;
}
var distance = percent / 100 * totalDist * 1000;
// time = ((percentage/100) * totalTIme/60).toFixed(2);
// alert("Time:"+time+" totalTime:"+totalTime+" totalDist:"+totalDist+" dist:"+distance);
if (!marker) {
marker = createMarker(polyline.GetPointAtDistance(distance), "percent: " + percent, "marker");
} else {
marker.setPosition(polyline.GetPointAtDistance(distance));
marker.setTitle("percent:" + percent);
marker.contentString = "<b>percent: " + percent + "</b><br>distance: " + (distance / 1000).toFixed(2) + " km<br>marker";
google.maps.event.trigger(marker, "click");
}
}
google.maps.event.addDomListener(window, 'load', initialize);
// from epoly_v3.js
// modified to use geometry library for length of line segments
// === 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.Polyline.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 += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), 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);
}
html {
height: 100%
}
body {
height: 100%;
margin: 0px;
padding: 0px
}
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=geometry"></script>
<div id="tools">
start:
<input type="text" name="start" id="start" value="Hyderabad" /> end:
<input type="text" name="end" id="end" value="Bangalore" />
<input type="submit" onclick="calcRoute();" /><br /> percentage:
<input type="text" name="percent" id="percent" value="0" />
<input type="submit" onclick="putMarkerOnRoute(parseFloat(document.getElementById('percent').value));" /> total time:<input type="text" name="totalTime" id="totalTime" value="0" />
</div>
<div id="map_canvas" style="float:left;width:70%;height:100%;"></div>
<div id="control_panel" style="float:right;width:30%;text-align:left;padding-top:20px">
<div id="directions_panel" style="margin:20px;background-color:#FFEE77;"></div>
<div id="total"></div>
</div>

Related

Leaflet-markercluster default icons missing

Default icons not showing for MarkerCluster plugin after using IconCreateFunction.
I want to use the default icons for the plugin but when using attached code I loose all the icons functions, I only get the numbers with no icons and if I activate the "childCount" I get one type of circle with the numbers offcenter within the icon. The markers has already been clustered and I want to add this value to the markercluster that is why I'm using the IconCreateFuncton so the numbers on the map shows correctly but I have lost all the icons and its beautiful functions... what is missing?
Result below using "var childCount"
$.getJSON("../test/test.geojson", function(json) {
geoLayer = L.geoJson(json, {
pointToLayer: function(feature, latlng) {
var log_p = feature.properties.log_p;
var marker;
if (log_p > 0){
marker = new L.shapeMarker(latlng, {radius: log_p*25, fillColor: '#2b83ba', fillOpacity: 0.5, color: '#000000', weight: 1, shape: 'circle'});
}
else {
marker = null
}
return marker;
},
onEachFeature: function(feature, layer) {
var popupText = "Amount per day: " + '<b>' + feature.properties.total + '</b>';
layer.bindPopup(popupText, {
closeButton: true,
offset: L.point(0, -20)
});
layer.on('click', function() {
layer.openPopup();
});
},
});
var markers = new L.MarkerClusterGroup({
iconCreateFunction: function(cluster) {
var children = cluster.getAllChildMarkers();
var sum = 0;
for (var i = 0; i < children.length; i++) {
sum += children[i].feature.properties.total;
}
/*
var childCount = cluster.getAllChildMarkers();
var c = ' marker-cluster-';
if (childCount < 10) {
c += 'small';
} else if (childCount < 500) {
c += 'medium';
} else {
c += 'large';
}
*/
return new L.DivIcon({ html: '<b>' + sum + '</b>', className: 'marker-cluster'/* + c */, iconSize: new L.Point(40, 40) });
}
});
markers.addLayer(geoLayer)
map.addLayer(markers);
});
Markercluster icons, styles and functions are lost
I manage to solve the problem, a few lines of code was missing. I added them to the original JavaScript code as follows.
$.getJSON("../test/test.geojson", function(json) {
geoLayer = L.geoJson(json, {
pointToLayer: function(feature, latlng) {
var log_p = feature.properties.log_p;
var marker;
if (log_p > 0) {
marker = new L.shapeMarker(latlng, {
radius: log_p * 25,
fillColor: '#2b83ba',
fillOpacity: 0.5,
color: '#000000',
weight: 1,
shape: 'circle'
});
} else {
marker = null
}
return marker;
},
onEachFeature: function(feature, layer) {
var popupText = "Amount per day: " + '<b>' + feature.properties.total + '</b>';
layer.bindPopup(popupText, {
closeButton: true,
offset: L.point(0, -20)
});
layer.on('click', function() {
layer.openPopup();
});
},
});
var clusters = new L.MarkerClusterGroup({
maxClusterRadius: 125,
iconCreateFunction: function(cluster) {
var children = cluster.getAllChildMarkers();
var sum = 0;
for (var i = 0; i < children.length; i++) {
sum += children[i].feature.properties.total;
}
var childCount = cluster.getChildCount()
var c = ' marker-cluster-';
if (childCount + sum <= 50) {
c += 'small';
} else if (childCount + sum <= 250) {
c += 'medium';
} else {
c += 'large';
}
return new L.DivIcon({
html: '<div><span>' + sum + '</span></div>',
className: 'marker-cluster marker-cluster-' + c,
iconSize: new L.Point(40, 40)
});
},
});
clusters.addLayer(geoLayer)
map.addLayer(clusters);
});

Change the color of the line between 2 waypoints

I use this function to draw my route between A and Z via the Waypoints array.
Is it possible to change the line color (default blue) but just between certain waypoints ?
I mean i want blue between A and B, red between C and D, ....
I found how to change the color of the line
var directionsDisplay = new google.maps.DirectionsRenderer({polylineOptions: { strokeColor: "#8b0013" },
but i can't find how to do it in waypoints...?
thanks for your help
function calcRoute(origin_lat,origin_lng,destination_lat,destination_lng) {
console.log ("Entrée CALC ROUTE");
var origin = new google.maps.LatLng(origin_lat,origin_lng);
var destination = new google.maps.LatLng(destination_lat,destination_lng);
var waypointsArray = document.getElementById('waypoints').value.split("|");
var waypts = [];
for (i = 0; i < waypointsArray.length; i++) {
if (waypointsArray[i]!="") {
var waypoint = waypointsArray[i];
var waypointArray = waypoint.split(",");
var waypointLat = waypointArray[0];
var waypointLng = waypointArray[1];
console.log("waypts lat " + waypointLat);
console.log("waypts lng " + waypointLng);
waypts.push({
location: new google.maps.LatLng(waypointLat,waypointLng),
stopover: true
})
}
}
console.log("waypts " + waypts.length);
var request = {
origin:origin,
destination:destination,
travelMode: google.maps.TravelMode.DRIVING,
waypoints: waypts,
provideRouteAlternatives: true
};
console.log ("Calc request "+JSON.stringify(request));
directionsService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(result);
}
});
}
You can't modify just a single segment of the output of the DirectionsRenderer. You can either render each segment with a separate DirectionsRenderer or create your own custom renderer that allows you to create individual polylines for each step of the route and color each one independently.
proof of concept fiddle with a custom renderer
code snippet:
var map;
var directionsService;
var directionsDisplay;
function initialize() {
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
directionsService = new google.maps.DirectionsService();
directionsDisplay = new google.maps.DirectionsRenderer({
map: map,
suppressPolylines: true
});
// Baltimore, MD, USA (39.2903848, -76.61218930000001)
// Boston, MA, USA (42.3600825, -71.05888010000001)
// Philadelphia, PA, USA (39.9525839, -75.16522150000003)
// New York, NY, USA (40.7127837, -74.00594130000002)
calcRoute(39.2903848, -76.6121893, 42.3600825, -71.05888);
}
google.maps.event.addDomListener(window, "load", initialize);
function calcRoute(origin_lat, origin_lng, destination_lat, destination_lng) {
console.log("Entrée CALC ROUTE");
var origin = new google.maps.LatLng(origin_lat, origin_lng);
var destination = new google.maps.LatLng(destination_lat, destination_lng);
var waypointsArray = document.getElementById('waypoints').value.split("|");
var waypts = [];
for (i = 0; i < waypointsArray.length; i++) {
if (waypointsArray[i] != "") {
var waypoint = waypointsArray[i];
var waypointArray = waypoint.split(",");
var waypointLat = waypointArray[0];
var waypointLng = waypointArray[1];
console.log("waypts lat " + waypointLat);
console.log("waypts lng " + waypointLng);
waypts.push({
location: new google.maps.LatLng(waypointLat, waypointLng),
stopover: true
})
}
}
console.log("waypts " + waypts.length);
var request = {
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING,
waypoints: waypts,
provideRouteAlternatives: true
};
console.log("Calc request " + JSON.stringify(request));
directionsService.route(request, customDirectionsRenderer);
}
function customDirectionsRenderer(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
var polyline = new google.maps.Polyline({
map: map,
strokeColor: "blue",
path: []
})
if (i == 1) {
polyline.setOptions({
strokeColor: "red"
});
}
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);
}
};
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<input id="waypoints" value="39.9525839,-75.1652215|40.7127837,-74.0059413" />
<div id="map_canvas"></div>

Calculating the distance between 4 locations [duplicate]

I'm trying to calculate the total distance for a route with a single waypoint in it, but somehow my code only returns the distance to the first waypoint instead of the total distance.
Here's my code:
function calcRoute(homebase,from,to,via){
var start = from;
var end = to;
var wypt = [ ];
wypt.push({
location:via,
stopover:true});
var request = {
origin:start,
destination:end,
waypoints:wypt,
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.DRIVING,
unitSystem: google.maps.DirectionsUnitSystem.METRIC
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var distance = response.routes[0].legs[0].distance.text;
var time_taken = response.routes[0].legs[0].duration.text;
var calc_distance = response.routes[0].legs[0].distance.value;
}
});
}
The reason you only get the distance for legs[0] is because that is what your code is calculating. You need to sum up the distances of all the legs:
http://www.geocodezip.com/v3_GoogleEx_directions-waypoints_totalDist.html
code snippet:
var directionDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer();
var chicago = new google.maps.LatLng(41.850033, -87.6500523);
var myOptions = {
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: chicago
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
directionsDisplay.setMap(map);
calcRoute();
}
google.maps.event.addDomListener(window, 'load', initialize);
function calcRoute() {
var request = {
// from: Blackpool to: Preston to: Blackburn
origin: "Blackpool,UK",
destination: "Blackburn,UK",
waypoints: [{
location: "Preston,UK",
stopover: true
}],
optimizeWaypoints: true,
travelMode: google.maps.DirectionsTravelMode.WALKING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById("directions_panel");
summaryPanel.innerHTML = "";
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += "<b>Route Segment: " + routeSegment + "</b><br />";
summaryPanel.innerHTML += route.legs[i].start_address + " to ";
summaryPanel.innerHTML += route.legs[i].end_address + "<br />";
summaryPanel.innerHTML += route.legs[i].distance.text + "<br /><br />";
}
computeTotalDistance(response);
} else {
alert("directions response " + status);
}
});
}
function computeTotalDistance(result) {
var totalDist = 0;
var totalTime = 0;
var myroute = result.routes[0];
for (i = 0; i < myroute.legs.length; i++) {
totalDist += myroute.legs[i].distance.value;
totalTime += myroute.legs[i].duration.value;
}
totalDist = totalDist / 1000.
document.getElementById("total").innerHTML = "total distance is: " + totalDist + " km<br>total time is: " + (totalTime / 60).toFixed(2) + " minutes";
}
html {
height: 100%
}
body {
height: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.google.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="map_canvas" style="float:left;width:70%;height:100%;"></div>
<div id="control_panel" style="float:right;width:30%;text-align:left;padding-top:20px">
<div id="directions_panel" style="margin:20px;background-color:#FFEE77;"></div>
<div id="total"></div>
</div>

How delete vertex (node) on Polygon with Hole (Google Maps V3)

I have taken this example (solution) from Ian Grainger, but I added a Polygon with inner hole.
This example work fine for outer vertex path, but not for inner vertex path.
I need implement event Listener for internal and external node, because on fire event on internal vertex, delete external vertex. It doesn't work well.
Can someone provide some sample on how to solve this issue?
One of your issues is polygon with a hole in it has multiple (in this case two) paths, and you aren't adding listeners for changes on both paths. Below is a proof of concept, it isn't perfect, sometimes markers are orphaned, but should be a starting point. Double click on the blue markers underneath the vertices to delete them (I couldn't get your "X" to reliably work).
proof of concept fiddle
updated proof of concept with white markers
code snippet:
var G = google.maps;
var zoom = 8;
var centerPoint = new G.LatLng(37.286172, -121.80929);
var map;
$(function() {
// create options object
var myOptions = {
center: centerPoint,
zoom: zoom,
mapTypeId: G.MapTypeId.ROADMAP
};
// create map with options
map = new G.Map($("#map_canvas")[0], myOptions);
addPolygon(map);
});
function addPolygon(map) {
var paths = [
[new G.LatLng(37.686172, -122.20929),
new G.LatLng(37.696172, -121.40929),
new G.LatLng(36.706172, -121.40929),
new G.LatLng(36.716172, -122.20929),
new G.LatLng(37.686172, -122.20929)
],
[new G.LatLng(37.486172, -122.00929),
new G.LatLng(37.086172, -122.00929),
new G.LatLng(37.086172, -121.60929),
new G.LatLng(37.486172, -121.60929),
new G.LatLng(37.486172, -122.00929)
]
];
poly = new G.Polygon({
clickable: false,
paths: paths,
map: map
});
polygonBinder(poly);
poly.setEditable(true);
G.event.addListener(poly.getPaths().getAt(0), 'insert_at', addClickMarker0);
G.event.addListener(poly.getPaths().getAt(1), 'insert_at', addClickMarker1);
}
function polygonBinder(poly) {
poly.binder0 = new MVCArrayBinder(poly.getPaths().getAt(0));
poly.binder1 = new MVCArrayBinder(poly.getPaths().getAt(1));
poly.markers = [];
for (var i = 0; i < poly.getPaths().getLength(); i++) {
poly.markers[i] = [];
for (var j = 0; j < poly.getPaths().getAt(i).getLength(); j++) {
var mark = new G.Marker({
map: map,
icon: {
path: G.SymbolPath.CIRCLE,
scale: 8,
strokeWeight: 2,
strokeColor: 'blue',
fillColor: 'blue',
fillOpacity: 1
},
draggable: true,
title: "double click to delete [" + i + "," + j + "]",
position: poly.getPaths().getAt(i).getAt(j)
});
poly.markers[i][j] = mark;
mark.bindTo('position', poly["binder" + i], (j).toString());
G.event.addListener(mark, "dblclick", deleteMark);
}
}
}
function addClickMarker0(index) {
addClickMarker(index, 0);
}
function addClickMarker1(index) {
addClickMarker(index, 1);
}
function deleteMark(evt) {
var minDist = Number.MAX_VALUE;
var minPathIdx = -1;
var minIdx = -1;
var i, j;
for (i = 0; i < poly.getPaths().getLength(); i++) {
for (j = 0; j < poly.getPaths().getAt(i).getLength(); j++) {
var distance = G.geometry.spherical.computeDistanceBetween(evt.latLng, poly.getPaths().getAt(i).getAt(j));
if (distance < minDist) {
minDist = distance;
minPathIdx = i;
minIdx = j;
}
if (distance < 10) {
document.getElementById('info').innerHTML = "deleted path=" + i + " idx=" + j + " dist<10 minDist=" + minDist + " meters";
poly.getPaths().getAt(i).removeAt(j);
break;
}
}
}
if ((i == poly.getPaths().getLength()) && (j == poly.getPaths(i - 1).getLength())) {
poly.getPaths().getAt(minPathIdx).removeAt(minIdx);
document.getElementById('info').innerHTML = "deleted path=" + minPathIdx + " idx=" + minIdx + " dist=" + minDist + " meters";
}
this.setMap(null);
}
function addClickMarker(index, pathIdx) {
var path = this;
// rebind binder
for (var i = 0; i < poly.markers[pathIdx].length; i++) {
poly.markers[pathIdx][i].setMap(null);
}
poly.markers[pathIdx] = [];
for (var i = 0; i < poly.getPaths().getAt(pathIdx).getLength(); i++) {
var mark = new G.Marker({
map: map,
icon: {
path: G.SymbolPath.CIRCLE,
scale: 8,
strokeWeight: 2,
strokeColor: 'blue',
fillColor: 'blue',
fillOpacity: 1
},
draggable: true,
title: "double click to delete [" + pathIdx + "," + i + "]",
position: poly.getPaths().getAt(pathIdx).getAt(i)
});
poly.markers[pathIdx][i] = mark;
mark.bindTo('position', poly["binder" + pathIdx], (i).toString());
G.event.addListener(mark, "dblclick", deleteMark);
}
}
function MVCArrayBinder(mvcArray) {
this.array_ = mvcArray;
}
MVCArrayBinder.prototype = new google.maps.MVCObject();
MVCArrayBinder.prototype.get = function(key) {
if (!isNaN(parseInt(key))) {
return this.array_.getAt(parseInt(key));
} else {
this.array_.get(key);
}
}
MVCArrayBinder.prototype.set = function(key, val) {
if (!isNaN(parseInt(key))) {
this.array_.setAt(parseInt(key), val);
} else {
this.array_.set(key, val);
}
}
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0;
padding: 0
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=drawing,geometry"></script>
<div id="info"></div>
<div id="map_canvas" style="width:100%; height:100%"></div>

Changing Marker Icons while using MarkerClusterer (Works in IE9 and Chrome, Doesn't Work in Firefox or earlier IE versions)

I have a public-facing site using GoogleMaps API v3 and the MarkerClusterer library. The page that is having a problem can be found here (http://www.mihomes.com/Find-Your-New-Home/San-Antonio-Homes). If you view this page in IE9 or Chrome, the correct pin icon images are shown, while in previous IE versions and Firefox different (and incorrect) pin icons.
IE9
Firefox 3.6.8
Here is the JavaScript code that generates the pins/clustering:
$(function() {
var dl_grid = new DLGrid(".GMapGrid");
dl_grid.init();
var dl_map = new DLMap("#map");
dl_map.init();
var markers = dl_grid.CollectMarkerData();
dl_map.LoadMarkers(markers);
});
var DLIcons = new function() {
var me = this;
me.NormalIcon = "/images/GoogleMapsIcons/mi_icon_n.png";
me.HoverIcon = "/images/GoogleMapsIcons/new_mi_icon_r.png";
me.ClusterIcon = "/images/GoogleMapsIcons/mi_icon_n.png";
me.ClusterHoverIcon = "/images/GoogleMapsIcons/new_mi_icon_r.png";
me.SalesCenterIcon = "/images/GoogleMapsIcons/new_mi_icon_n2.gif";
me.DesignCenterIcon = "/images/GoogleMapsIcons/mi_dc_n.png";
me.DesignCenterHoverIcon = "/images/GoogleMapsIcons/mi_dc_r.png";
};
//Used for all functions relating to the table below the map
var DLGrid = function(grid_selector) {
//Initialize variables
var me = this;
me.grid = $(grid_selector);
//Initialize
me.init = function() {
setupTableSorting();
setupHoverEvents();
setupZoomButtons();
};
//Setup the table sorting
var setupTableSorting = function() {
//Init tablesorter plugin
var sort_options = { headers: { 0: { sorter: false }, 4: { sorter: false} } };
if (MI_DIVISION_LANDING_TABLE_SORT_OPTIONS != undefined) {
sort_options = MI_DIVISION_LANDING_TABLE_SORT_OPTIONS;
}
me.grid.tablesorter(sort_options);
//As soon as the user sorts, remove all Community Groups
me.grid.bind("sortEnd", function() {
me.grid.find("tr.communityGroup").remove();
$("tr.groupedCommunity").removeClass("groupedCommunity").addClass("ungroupedCommunity");
//$("tr.ungroupedCommunity").removeClass("ungroupedCommunity");
me.grid.trigger("update");
});
};
var highlightRow = function(marker) {
var markerId = (typeof (marker) == "string") ? marker : marker.jsonData.MarkerID;
$(me.grid).find("#" + markerId).addClass("highlightedRow");
};
// Bind to mouseover/mouseout events and highlight the proper row in the table
// Trigger mouseover/mouseout events when you hover over a row in the table
var setupHoverEvents = function() {
$(document).bind("MARKER_MOUSEOVER", function(e, marker) {
$(me.grid).find("tbody tr.highlightedRow").removeClass("highlightedRow");
if (typeof (marker) != "string" && marker.length != undefined) {
for (var i = 0; i < marker.length; i++) {
highlightRow(marker[i]);
}
}
else {
highlightRow(marker);
}
});
// $(document).bind("MULTIPLE_MARKER_MOUSEOVER", function(e, markers) {
// $(me.grid).find("tbody tr.highlightedRow").removeClass("highlightedRow");
// for (var i = 0; i < markers.length; i++) {
// var markerId = (typeof (markers[i]) == "string") ? markers[i] : markers[i].jsonData.MarkerID;
// $(me.grid).find("#" + markerId).addClass("highlightedRow");
// }
// });
$(me.grid).find("tbody tr").mouseover(function() {
$(document).trigger("MARKER_MOUSEOVER", [$(this).attr("id")]);
});
};
// The zoom buttons next to each row should zoom to a marker and show it's info window
var setupZoomButtons = function() {
$(me.grid).find("tbody tr .zoom_link img").click(function() {
$(document).trigger("MAP_SHOW_MARKER_POPUP_AND_ZOOM", [$(this).parent().parent().attr("id")]);
});
};
// Collect and parse the JSON data from the hidden 'data' column in the table
me.CollectMarkerData = function() {
var markers = [];
$.each(me.grid.find("tbody tr:not(.communityGroup)"), function(i, row) {
var dataCell = $(row).children("td.data");
var rawContent = $(dataCell).text();
var json_data = {};
if (rawContent.length > 0) {
json_data = JSON.parse(rawContent);
}
json_data["MarkerID"] = $(row).attr("id");
markers.push(json_data);
});
return markers;
};
};
//Used for all functions relating to map
var DLMap = function(map_div_selector) {
//Initialize variables
var me = this;
me.mapDivSelector = map_div_selector;
me.mapObj;
me.init = function() {
setupMap();
bindHoverEvents();
setupPopupEvents();
setupDesignCenter();
};
//Basic setup of map
var setupMap = function() {
me.mapObj = new DLGoogleMap(me.mapDivSelector);
me.mapObj.init(onMarkerMouseOver, showMarkerPopup);
};
// Add an array of markers (from json data) to the map
me.LoadMarkers = function(markers) {
$.each(markers, function(i, json_marker) {
me.mapObj.addMarker(json_marker);
});
me.mapObj.fitMapToMarkers();
};
var showMarkerPopup = function(markerJson, zoomToLocation) {
var source = $("#MapMarkerInfoWindow").html();
var template = Handlebars.compile(source);
var content = template(markerJson);
var triggerKey = (zoomToLocation == true) ? "MAP_SHOW_POPUP_AND_ZOOM" : "MAP_SHOW_POPUP";
$(document).trigger(triggerKey, [content, markerJson.Lat, markerJson.Lng]);
};
var onMarkerMouseOver = function(markerJson) {
$(document).trigger("MARKER_MOUSEOVER", markerJson);
}
// Highlight (or unhighlight) a marker when a mouseover/mouseout event is triggered
var bindHoverEvents = function() {
$(document).bind("MARKER_MOUSEOVER", function(e, marker) {
if (typeof (marker) != "string" && marker.length != undefined) {
marker = marker[0];
}
me.mapObj.resetMarkerHighlighting();
me.mapObj.highlightMarker(marker);
});
// $(document).bind("MULTIPLE_MARKER_MOUSEOVER", function(e, markers) {
// me.mapObj.resetMarkerHighlighting();
// if (markers[0].cluster != null) {
// me.mapObj.highlightCluster(markers[0]
// }
// });
};
var setupPopupEvents = function() {
$(document).bind("MAP_SHOW_POPUP", function(e, content, lat, lng) {
me.mapObj.showPopup(content, lat, lng);
});
$(document).bind("MAP_SHOW_POPUP_AND_ZOOM", function(e, content, lat, lng) {
me.mapObj.showPopup(content, lat, lng, true);
});
$(document).bind("MAP_SHOW_MARKER_POPUP", function(e, marker) {
if (typeof (marker) == "string") {
marker = me.mapObj.findMarkerByID(marker);
}
showMarkerPopup(marker.jsonData);
});
$(document).bind("MAP_SHOW_MARKER_POPUP_AND_ZOOM", function(e, marker) {
if (typeof (marker) == "string") {
marker = me.mapObj.findMarkerByID(marker);
}
showMarkerPopup(marker.jsonData, true);
});
};
var setupDesignCenter = function() {
var jsonText = $.trim($("#DesignCenterData").text());
if (jsonText.length > 5) {
var dcJson = JSON.parse(jsonText);
me.mapObj.addDesignCenterMarker(dcJson);
}
};
};
var DLGoogleMap = function(map_div_selector) {
//Initialize variables
var me = this;
me.mapDiv = $(map_div_selector);
me.gmap;
me.markers = [];
me.markerClusterer;
me.infoWindow;
me.onMouseOver;
me.onClick;
me.ZOOM_TO_LEVEL = 14;
me.highlightedMarkers = [];
me.designCenterMarker = null;
//Extend Google Map Classes
google.maps.Marker.prototype.jsonData = null;
google.maps.Marker.prototype.iconImg = null;
google.maps.Marker.prototype.iconHoverImg = null;
me.init = function(onMouseOver, onClick) {
me.onMouseOver = onMouseOver;
me.onClick = onClick;
setupMap();
setupClustering();
setupDrivingDirectionLinks();
};
var setupMap = function() {
//var latlng = new google.maps.LatLng(40.05, -82.95);
var myOptions = {
zoom: 14,
scrollwheel: false,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
me.gmap = new google.maps.Map(document.getElementById("map"), myOptions);
me.infoWindow = new google.maps.InfoWindow();
};
var setupDrivingDirectionLinks = function() {
$("a.gDirectionsLink").live("click", function(e) {
e.preventDefault();
$(".gPopupInfo").hide();
$(".gDrivingDirections").show();
});
$("a.gCloseDrivingDirections").live("click", function(e) {
e.preventDefault();
$(".gDrivingDirections").hide();
$(".gPopupInfo").show();
});
};
//Add a single json marker to the map
me.addMarker = function(jsonMarker) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(jsonMarker.Lat, jsonMarker.Lng),
title: jsonMarker.Name
});
marker.jsonData = jsonMarker;
if (jsonMarker.HasSalesCenter == "True") {
marker.iconImg = DLIcons.SalesCenterIcon;
}
else {
marker.iconImg = DLIcons.NormalIcon;
}
marker.iconHoverImg = DLIcons.HoverIcon;
marker.icon = marker.iconImg;
google.maps.event.addListener(marker, 'click', function() {
me.onClick(marker.jsonData);
});
google.maps.event.addListener(marker, 'mouseover', function() { me.onMouseOver(marker) });
me.markerClusterer.addMarker(marker);
me.markers.push(marker);
};
//Add an arbitrary marker
me.addDesignCenterMarker = function(dcJson) {
me.designCenterMarker = new google.maps.Marker({
position: new google.maps.LatLng(dcJson.Lat, dcJson.Lng),
title: "Design Center",
map: me.gmap
});
me.designCenterMarker.jsonData = dcJson;
me.designCenterMarker.iconImg = DLIcons.DesignCenterIcon;
me.designCenterMarker.iconHoverImg = DLIcons.DesignCenterHoverIcon;
me.designCenterMarker.icon = me.designCenterMarker.iconImg;
google.maps.event.addListener(me.designCenterMarker, 'mouseover', function() {
me.highlightMarker(me.designCenterMarker);
});
google.maps.event.addListener(me.designCenterMarker, 'mouseout', function() {
me.unHighlightMarker(me.designCenterMarker);
});
google.maps.event.addListener(me.designCenterMarker, 'click', function() {
me.infoWindow.close();
var source = $("#DesignCenterInfoWindow").html();
var template = Handlebars.compile(source);
var content = template(me.designCenterMarker.jsonData);
me.showPopup(content, me.designCenterMarker.jsonData.Lat, me.designCenterMarker.jsonData.Lng);
});
};
me.resetMarkerHighlighting = function() {
for (var i = 0; i < me.highlightedMarkers.length; i++) {
me.unHighlightMarker(me.highlightedMarkers[i]);
}
me.highlightedMarkers = [];
}
me.highlightMarker = function(m) {
var marker = (typeof (m) == "string") ? me.findMarkerByID(m) : m;
if (marker != null) {
if (marker.cluster == null || (marker.cluster.hasMultipleMarkers() == false)) {
marker.setIcon(marker.iconHoverImg);
}
else {
highlightCluster(marker.cluster);
}
me.highlightedMarkers.push(marker);
}
};
me.unHighlightMarker = function(m) {
var marker = (typeof (m) == "string") ? me.findMarkerByID(m) : m;
if (marker != null) {
if (marker.cluster == null || (marker.cluster.hasMultipleMarkers() == false)) {
marker.setIcon(marker.iconImg);
}
else {
unHighlightCluster(marker.cluster);
}
}
};
me.zoomAndShowPopup = function(content, lat, lng) {
};
me.showPopup = function(content, lat, lng, zoomToLocation) {
if (zoomToLocation == true) {
var adjustedLat = lat - (me.gmap.getZoom() * 0.01);
me.zoomToLocation(adjustedLat, lng);
}
me.infoWindow.setContent(content);
me.infoWindow.setPosition(new google.maps.LatLng(lat, lng));
me.infoWindow.open(me.gmap);
};
me.zoomToLocation = function(lat, lng) {
me.gmap.setZoom(me.ZOOM_TO_LEVEL)
me.gmap.setCenter(new google.maps.LatLng(lat, lng));
};
me.fitMapToMarkers = function() {
me.markerClusterer.fitMapToMarkers();
};
//Setup the map 'clustering', so that nearby markers will cluster together to form 1 icon
var setupClustering = function() {
Cluster.prototype.iconImg = DLIcons.ClusterIcon;
Cluster.prototype.iconHoverImg = DLIcons.ClusterHoverIcon;
var mc_options = {
gridSize: 15,
maxZoom: 15,
zoomOnClick: false,
styles: [{ url: DLIcons.ClusterIcon, height: 30, width: 20, textColor: "#fff", textSize: 10}],
showMarkerCount: false,
onClusterAdded: onClusterAdded
};
me.markerClusterer = new MarkerClusterer(me.gmap, [], mc_options);
//Setup Cluster Info Windows with a list of marker links
google.maps.event.addListener(me.markerClusterer, 'clusterclick', function(cluster) {
me.infoWindow.close();
var source = $("#MapClusterInfoWindow").html();
var template = Handlebars.compile(source);
var markers = getClusterJsonMarkers(cluster);
var data = {
markers: markers
};
var content = template(data);
$(document).trigger("MAP_SHOW_POPUP", [content, cluster.getCenter().lat(), cluster.getCenter().lng()]);
});
//Setup Cluster marker highlighting
google.maps.event.addListener(me.markerClusterer, 'clustermouseover', function(cluster) {
me.resetMarkerHighlighting();
highlightCluster(cluster);
var cmarkers = cluster.getMarkers();
$(document).trigger("MARKER_MOUSEOVER", [cmarkers]);
});
$(".openMarkerPopup").live("click", function(e) {
e.preventDefault();
$(document).trigger("MAP_SHOW_MARKER_POPUP", [$(this).attr("rel")]);
});
};
var onClusterAdded = function(cluster) {
if (clusterHasSalesCenter(cluster)) {
cluster.iconImg = DLIcons.SalesCenterIcon;
cluster.updateIconUrl(cluster.iconImg);
}
else {
cluster.iconImg = DLIcons.ClusterIcon;
}
};
var clusterHasSalesCenter = function(cluster) {
if ((cluster == undefined) || (cluster.markers_ == undefined)) {
return false;
}
for (var i = 0; i < cluster.markers_.length; i++) {
if (cluster.markers_[i].jsonData.HasSalesCenter == "True") {
return true;
}
}
return false;
};
var highlightCluster = function(cluster) {
//me.highlightedMarkers = [];
// for (var i = 0; i < cluster.markers_.length; i++) {
// me.highlightedMarkers.push(cluster.markers_[i]);
// }
cluster.updateIconUrl(cluster.iconHoverImg);
};
var unHighlightCluster = function(cluster) {
cluster.updateIconUrl(cluster.iconImg);
};
var getClusterJsonMarkers = function(cluster) {
var jsonMarkers = [];
var gmarkers = cluster.getMarkers();
for (var i = 0; i < gmarkers.length; i++) {
jsonMarkers.push(gmarkers[i].jsonData);
}
return jsonMarkers;
};
// Get a marker on the map given it's MarkerID
me.findMarkerByID = function(markerId) {
for (var i = 0; i < me.markers.length; i++) {
if (me.markers[i].jsonData.MarkerID == markerId) {
return me.markers[i];
break;
}
}
return null;
};
};
This website is built using .NET Framework 3.5 and ASP.NET WebForms.
Thanks for the assistance.

Resources