Google maps v3: clustering with custom markers - javascript

I'm trying to use MarkerClusterer to clusterize the markers on my map.
The problem is that I'm not using default markers (google.maps.Marker), but instead a custom class which hinerits from google.maps.OverlayView.
Unfortunately it seems that the library has been developed assuming the use of basic markers, in fact I get errors because my class does not implement methods defined in google.maps.Marker.
Is it possible to use the MarkerClusterer by keeping my custom markers?
EDIT: it was a lot easier than I expected, I solved by implementing 2 methods in my custom class:
setVisible() and getPosition()
in order to help others the following is my complete interface (without full implementation):
BFPushpin = function(config)
{
this.setMap(config.map);
this.set("position", config.position);
// other settings...
};
// my class extends google.maps.OverlayView
BFPushpin.prototype = new google.maps.OverlayView();
BFPushpin.prototype.getBounds = function()
{
return new google.maps.LatLngBounds(this.position, this.position);
};
BFPushpin.prototype.getPoint = function()
{
var bounds = this.getBounds();
var projection = this.getProjection();
var sw = projection.fromLatLngToDivPixel(bounds.getSouthWest());
var ne = projection.fromLatLngToDivPixel(bounds.getNorthEast());
return new google.maps.Point(sw.x, ne.y);
};
BFPushpin.prototype.getSuperContainer = function()
{
var panes = this.getPanes();
return jQuery(panes ? panes.overlayImage : "");
};
BFPushpin.prototype.getContainer = function()
{
// return inner container
};
BFPushpin.prototype._generatePopupContent = function()
{
// return markup for the popupwindow
};
BFPushpin.prototype._addListeners = function()
{
// add handlers for the pushpin
};
BFPushpin.prototype.onAdd = function()
{
// customize content here
};
BFPushpin.prototype.onRemove = function()
{
// remove pin container here
};
BFPushpin.prototype.draw = function()
{
// set display style here
};
BFPushpin.prototype.setVisible = function(visible)
{
// set display block or hidden
};
BFPushpin.prototype.getPosition = function()
{
return this.position;
};

Or just define the functions that the MarkerClusterer expects on the marker. setMap and getPosition() and some other ones.

You should probably define your new marker class in such a way that it also inherits from google.maps.Marker (i.e. that it implements its interface). It is logical that MarkerClusterer uses this interface - it has to suppose the markers are markers in order to work with them :-)

Hopefully this will also help people trying to get this solution to work. Thanks #daveoncode for the example. I was able to modify it to get it working for me:
renderMap() {
const mapProperties = {
center: new google.maps.LatLng(34.0234, -84.6155),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.map = new google.maps.Map(this.mapElement.nativeElement, mapProperties);
let markers = [];
for (let i=0; i<this._sitesList.length; i++) {
let bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(this._sitesList[i].lat, this._sitesList[i].lon)
);
let position = new google.maps.LatLng(this._sitesList[i].lat, this._sitesList[i].lon);
let html = this.makeHtmlForSitePanel(i, this._sitesList[i]); // I have a function to return html for my OverlayView panels here.
markers.push( this.generateSitePanel(bounds, html, this.map, position) );
}
var markerCluster = new MarkerClusterer(this.map, markers, {
imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
});
}
generateSitePanel(bounds, html, map, position) {
SitePanel.prototype = new google.maps.OverlayView();
function SitePanel (bounds, html, map, position) {
this.bounds_ = bounds;
this.set('position', position);
this.html_ = html;
this.map_ = map;
this.div_ = null;
this.setMap(map);
}
SitePanel.prototype.getBounds = function() {
return new google.maps.LatLngBounds(this.position, this.position);
};
SitePanel.prototype.getPoint = function() {
var bounds = this.getBounds();
var projection = this.getProjection();
var sw = projection.fromLatLngToDivPixel(bounds.getSouthWest());
var ne = projection.fromLatLngToDivPixel(bounds.getNorthEast());
return new google.maps.Point(sw.x, ne.y);
};
SitePanel.prototype.getSuperContainer = function(){
var panes = this.getPanes();
return $(panes ? panes.overlayImage : '');
};
SitePanel.prototype.getContainer = function()
{
// return inner container
// I don't have anything for this one
};
SitePanel.prototype.getPosition = function() {
return this.position;
};
SitePanel.prototype.onAdd = function() {
var div = document.createElement('div');
div.className = 'tooltip-container-';
div.innerHTML = this.html_;
div.style.position = 'absolute';
this.div_ = div;
var panes = this.getPanes();
panes.overlayImage.appendChild(div);
};
SitePanel.prototype.draw = function() {
var overlayProjection = this.getProjection();
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
var div = this.div_;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 20 + 'px';
};
SitePanel.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
return new SitePanel(bounds, html, map, position);
}

Related

returning object from a javascript function with database query inside

I am trying to return the markers as the object but when i run the function it just returns [ ], but printing it inside i can see the object data, can anyone explain how to return the object batch2 please?
google.maps.event.addListener(mgr, 'loaded', function(){
mgr.addMarkers(getMarkers(),6); //add all the markers! documentation for viewports with totals for city count, look at viewport
mgr.addMarkers(getMarkers2(),14); //get markers for zoomed out place, add click function to zoom in
//mgr.addMarkers(getMarkers(1000), 8);
console.log("added");
mgr.refresh();
});
function getMarkers2() {
var batch2 = [];
var clusters = new Parse.Query("cityfreqcoords");
var clusterresults = new Parse.Object("cityfreqcoords");
clusters.find({
success: function (results) {
for (i = 1; i < results.length; i++) {
var city = (results[i]["attributes"]["city"]);
var count = (results[i]["attributes"]["count"]);
var lat = (results[i]["attributes"]["lat"]);
var lng = (results[i]["attributes"]["lng"]);
var markerLatlong = new google.maps.LatLng(lat, lng);
//icon =
//adding the marker
var marker2 = new google.maps.Marker({
position: markerLatlong,
title: city,
clickable: true,
animation: google.maps.Animation.DROP
//icon:icon
});
//adding the click event and info window
google.maps.event.addListener(marker2, 'click', function () {
map.setZoom(6);
map.setCenter(marker2.getPosition());
});
batch2.push(marker2);
}
}
})
return batch2;
}
It would appear that clusters.find is asynchronous. You return batch2 before cluster.find succeeds. There are a handful of patterns for working with asynchronous code in JavaScript -- one common one is to use a callback. You would need to rewrite your code like so:
function getMarkers2(callback) {
var batch2 = [];
var clusters = new Parse.Query("cityfreqcoords");
var clusterresults = new Parse.Object("cityfreqcoords");
clusters.find({
success: function (results) {
for (i = 1; i < results.length; i++) {
var city = (results[i]["attributes"]["city"]);
var count = (results[i]["attributes"]["count"]);
var lat = (results[i]["attributes"]["lat"]);
var lng = (results[i]["attributes"]["lng"]);
var markerLatlong = new google.maps.LatLng(lat, lng);
//icon =
//adding the marker
var marker2 = new google.maps.Marker({
position: markerLatlong,
title: city,
clickable: true,
animation: google.maps.Animation.DROP
//icon:icon
});
//adding the click event and info window
google.maps.event.addListener(marker2, 'click', function () {
map.setZoom(6);
map.setCenter(marker2.getPosition());
});
batch2.push(marker2);
}
}
callback(batch2);
})
}
Then call it like so:
getMarkers2(function(markers) {
mgr.addMarkers(markers, 14);
});
If you're interested, take a look at how promises work as you might prefer that approach over using callbacks.
With callbacks in javascript, you generally don't return data. You pass in another function reference into the handler as a callback.
EG:
function getMarkers2(f) {
// do stuff
//when done
f(batch2)
}
Ended up just passing making the marker manager global and passing mgr into the query which worked, probably not the most efficient way to do it
function getMarkers2(mgr) {
Parse.initialize("X", "Y");
var batch2 = [];
var clusters = new Parse.Query("cityfrequency2");
var clusterresults = new Parse.Object("cityfrequency2");
clusters.find({
success: function (results) {
for (i = 0; i < (results.length); i++) {
var city = (results[i]["attributes"]["city"]);
var lat = (results[i]["attributes"]["lat"]);
var lng = (results[i]["attributes"]["lng"]);
var markerLatlong = new google.maps.LatLng(lat, lng);
var image = {
url: 'warning.png',
size: new google.maps.Size(50, 46),
// The origin
origin: new google.maps.Point(0, 0),
// The anchor
anchor: new google.maps.Point(25, 0)
};
//adding the marker
var marker2 = new google.maps.Marker({
position: markerLatlong,
title: city,
clickable: true,
animation: google.maps.Animation.DROP,
icon:image
});
//adding the click event and info window
google.maps.event.addListener(marker2, 'click', function () {
map.setZoom(6);
map.setCenter();
});
batch2.push(marker2);
mgr.addMarkers(batch2,0,6);
mgr.refresh();
}
}
})
}
function setupMarkers() {
var mgrOptions = { borderPadding: 50, maxZoom: 15, trackMarkers: true };
mgr = new MarkerManager(map,mgrOptions);
google.maps.event.addListener(mgr, 'loaded', function(){
getMarkers2(mgr);
getMarkers(mgr);
console.log("added");
});
}

IE Issue of Google Maps Marker Animation

I am using google maps api v3.
The Below code i am trying to run , It is working on all Browsers except IE.
Can u please suggest any changes needed to work in IE.
Fiddle Link
My Code is :
var map;
var mapOptions = { center: new google.maps.LatLng(0.0, 0.0), zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP };
var markers = [];
function initialize() {
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
from1 = new google.maps.LatLng(0,0);
to1 = new google.maps.LatLng(30,12);
from2 = new google.maps.LatLng(-30,15);
to2 = new google.maps.LatLng(10,-100);
from3 = new google.maps.LatLng(0,-50);
to3 = new google.maps.LatLng(0,50);
addMarker(from1,to1);
addMarker(from2,to2);
addMarker(from3,to3);
}
function addMarker(pos, dest) {
var marker = new google.maps.Marker({
map: map,
position: pos,
destination: dest
});
google.maps.event.addListener(marker, 'click', function(event) {
fromLat = this.position.lat();
fromLng = this.position.lng();
toLat = this.destination.lat();
toLng = this.destination.lng();
// store a LatLng for each step of the animation
frames = [];
for (var percent = 0; percent < 1; percent += 0.01) {
curLat = fromLat + percent * (toLat - fromLat);
curLng = fromLng + percent * (toLng - fromLng);
frames.push(new google.maps.LatLng(curLat, curLng));
}
move = function(marker, latlngs, index, wait, newDestination) {
marker.setPosition(latlngs[index]);
if(index != latlngs.length-1) {
// call the next "frame" of the animation
setTimeout(function() {
move(marker, latlngs, index+1, wait, newDestination);
}, wait);
}
else {
// assign new route
marker.position = marker.destination;
marker.destination = newDestination;
}
}
// begin animation, send back to origin after completion
move(marker, frames, 0, 20, marker.position);
});
markers.push(marker);
}
google.maps.event.addDomListener(window, 'load', initialize);
After some fiddling it looks like a typing issue. Because you haven't implicitly declared the variable frames as a var ie is unsure that it is an array, thus the error "Object does not support method push".
You simply need to change:
frames = [];
to:
var frames = [];
Tested in ie 8- 10.

Using Google Maps custom overlays to create custom icons (with Raphael JS)

I'm using Google Maps OverlayView class to create custom markers (with Raphael JS) and am having issues accessing certain properties of my new subclass when calling a public method.
I followed Google's fairly straightforward example here ~ https://developers.google.com/maps/documentation/javascript/overlays#CustomOverlays ~ to create a custom marker class, including its 'hide' and 'show' methods.
function MapCustomMarker(opts){
this.pos_ = opts.position;
this.map_ = opts.map;
this.div_ = null;
this.color_ = (!opts.color ? '#e32636' : opts.color);
this.height_ = 32;
this.width_ = 32;
this.scale_ = 1.2;
this.icons_ = {
pinpoint:'M16,3.5c-4.142,0-7.5,3.358-7.5,7.5c0,4.143,7.5,18.121,7.5,18.121S23.5,15.143,23.5,11C23.5,6.858,20.143,3.5,16,3.5z M16,14.584c-1.979,0-3.584-1.604-3.584-3.584S14.021,7.416,16,7.416S19.584,9.021,19.584,11S17.979,14.584,16,14.584z',
help: 'M12.558,15.254c2.362,0,4.277-1.916,4.277-4.279s-1.916-4.279-4.277-4.279c-2.363,0-4.28,1.916-4.28,4.279S10.194,15.254,12.558,15.254zM15.662,15.224c-0.875,0.641-1.941,1.031-3.103,1.031c-1.164,0-2.231-0.391-3.105-1.031c-0.75,0.625-1.498,1.519-2.111,2.623c-1.422,2.563-1.578,5.192-0.35,5.874c0.55,0.312,1.127,0.078,1.723-0.496c-0.105,0.582-0.166,1.213-0.166,1.873c0,2.938,1.139,5.312,2.543,5.312c0.846,0,1.265-0.865,1.466-2.188c0.201,1.311,0.62,2.188,1.462,2.188c1.396,0,2.544-2.375,2.544-5.312c0-0.66-0.062-1.291-0.167-1.873c0.598,0.574,1.174,0.812,1.725,0.496c1.228-0.682,1.069-3.311-0.353-5.874C17.159,16.742,16.412,15.849,15.662,15.224zM19.821,3.711l-1.414,1.414c1.499,1.499,2.428,3.569,2.428,5.851c0,2.283-0.929,4.353-2.428,5.853l1.413,1.412c1.861-1.86,3.015-4.43,3.015-7.265C22.835,8.142,21.683,5.572,19.821,3.711zM16.288,14.707l1.413,1.414c1.318-1.318,2.135-3.138,2.135-5.145c0-2.007-0.816-3.827-2.134-5.145l-1.414,1.414c0.956,0.956,1.547,2.275,1.547,3.731S17.243,13.751,16.288,14.707zM21.941,1.59l-1.413,1.414c2.042,2.042,3.307,4.862,3.307,7.971c0,3.11-1.265,5.93-3.308,7.972l1.413,1.414c2.405-2.404,3.895-5.725,3.895-9.386C25.835,7.315,24.346,3.995,21.941,1.59z'
}
this.popup_ = 'M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z';
this.icon_ = this.icons_[opts.icon];
this.setMap(opts.map);
}
MapCustomMarker.prototype = new google.maps.OverlayView();
MapCustomMarker.prototype.onAdd = function() {
// Create the DIV and set some basic attributes.
var div = document.createElement('div');
div.style.border = "none";
div.style.borderWidth = "0px";
div.style.position = "absolute";
div.style.cursor = "pointer";
div.style.width = this.width_+"px";
div.style.height = this.height_+"px";
var paper = Raphael(div,this.height_, this.width_);
var el = paper.path(Raphael.transformPath(this.icon_, 's'+this.scale_)).attr({fill: this.color_, stroke: "#333333"});
// Set the overlay's div_ property to this DIV
this.div_ = div;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayImage pane.
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(div);
}
MapCustomMarker.prototype.draw = function() {
// Size and position the overlay.
var overlayProjection = this.getProjection();
// We'll use these coordinates to position the DIV.
var o = overlayProjection.fromLatLngToDivPixel(this.pos_);
var l = o.x - Math.round(this.width_ / 2);
var t = o.y - this.height_;
this.div_.style.left = l + 'px';
this.div_.style.top = t + 'px';
}
MapCustomMarker.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
MapCustomMarker.prototype.hide = function() {
console.log(this.div_);
console.log(this.color_);
if (this.div_) {
this.div_.style.visibility = "hidden";
}
}
MapCustomMarker.prototype.show = function() {
if (this.div_) {
this.div_.style.visibility = "visible";
}
}
MapCustomMarker.prototype.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
This class creates markers on my map with the Raphael icons very nicely.
The problem comes when I want to hide or show any specific marker.
var marker = new MapCustomMarker({position: pos, map: self.map, icon:'help', color:'#e32636'});
marker.hide();
marker.hide() is not hiding the markers.
You'll notice in the "hide" method, I have two console.log commands testing the values of this.color_ and this.div_. console.log(this.color_) returns the color set when the object is initiated. console.log(this.div_) returns null even though it was obviously altered in the 'onAdd' and 'draw' methods when the marker was created.
I'm not sure if this is a misunderstanding of public and private properties in javascript or something else. I used the google maps custom overlay example almost exactly.
If anyone has any ideas, please pass them along. (And aside from this one issue, I hope this code will assist others who want to merge Raphael JS capabilities with Google Maps.)
Thanks!
So it appears that marker.hide() is being called before the methods 'onAdd' and 'draw' have a chance to alter the this.div_ variable when the object is instantiated. Since I want all markers to be hidden when they are created, I'm just going to add
div.style.visibility = "hidden";
to the onAdd method, and will call maker.show() later, well after all of the markers have been loaded.

Add Multiple Custom Control buttons on Google Maps API v3

I have 2 Google maps on the same page and I am looking to add the same button to a map that already has one.
This button is to close the overlay that Google maps launches in, basically an exit button.
The Button works fine on the first map but I cant get the second one to work D:!
I know I have to change the variables because that's what I had to do to get 2 maps on one page. Also if someone else is good in Google Maps API v3 can you tell me why I need the "map" variable in the function HomeControl(controlDiv, map) { line?
Here is my code:
function HomeControl(controlDiv, map) {
// Set CSS styles for the DIV containing the control
// Setting padding to 5 px will offset the control
// from the edge of the map.
controlDiv.style.padding = '5px';
// Set CSS for the control border.
var controlUI = document.createElement('div');
controlUI.style.backgroundColor = 'white';
controlUI.style.borderStyle = 'solid';
controlUI.style.borderWidth = '2px';
controlUI.style.cursor = 'pointer';
controlUI.style.textAlign = 'center';
controlUI.title = 'Click to set the map to Home';
controlDiv.appendChild(controlUI);
// Set CSS for the control interior.
var controlText = document.createElement('div');
controlText.style.fontFamily = 'Arial,sans-serif';
controlText.style.fontSize = '12px';
controlText.style.paddingLeft = '4px';
controlText.style.paddingRight = '4px';
controlText.innerHTML = '<strong>Home</strong>';
controlUI.appendChild(controlText);
// Setup the click event listeners: simply set the map to Chicago.
google.maps.event.addDomListener(controlUI, 'click', function() {
document.getElementById('light').style.display='none';
document.getElementById('fade').style.display='none';
});
}
function detectBrowser() {
var useragent = navigator.userAgent;
var mapdivMap = document.getElementById("light");
if (useragent.indexOf('iPhone') != -1 || useragent.indexOf('Android') != -1 ) {
mapdivMap.style.width = '100%';
mapdivMap.style.height = '100%';
initialize();
} else {
mapdivMap.style.width = '600px';
mapdivMap.style.height = '100%';
initialize();
}
};
function initialize() {
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(43.464258,-80.52041),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('light'),
mapOptions);
var marker = new google.maps.Marker({
  position: map.getCenter(),
    map: map,
    title: 'Waterloo'
});
// Create the DIV to hold the control and call the HomeControl() constructor
// passing in this DIV.
var homeControlDiv = document.createElement('div');
var homeControl = new HomeControl(homeControlDiv, map);
homeControlDiv.index = 1;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv);
}
//*******************************************************
//************ Conestoga Map Page JavaScript ************
//*******************************************************
function Map2(Map2Div, map2) {
// Set CSS styles for the DIV containing the control
// Setting padding to 5 px will offset the control
// from the edge of the map.
Map2Div.style.padding = '5px';
// Set CSS for the control border.
var Map2UI = document.createElement('div');
Map2UI.style.backgroundColor = 'white';
Map2UI.style.borderStyle = 'solid';
Map2UI.style.borderWidth = '2px';
Map2lUI.style.cursor = 'pointer';
Map2UI.style.textAlign = 'center';
Map2UI.title = 'Click to set the map to Home';
Map2Div.appendChild(Map2UI);
// Set CSS for the control interior.
var Map2Text = document.createElement('div');
Map2Text.style.fontFamily = 'Arial,sans-serif';
Map2Text.style.fontSize = '12px';
Map2Text.style.paddingLeft = '4px';
Map2Text.style.paddingRight = '4px';
Map2Text.innerHTML = '<strong>Home</strong>';
Map2UI.appendChild(Map2Text);
// Setup the click event listeners: simply set the map to Chicago.
google.maps.event.addDomListener(MapUI2, 'click', function() {
document.getElementById('light').style.display='none';
document.getElementById('fade').style.display='none';
});
}
function detectBrowser2() {
var useragent2 = navigator.userAgent;
var mapdivMap2 = document.getElementById("light");
if (useragent2.indexOf('iPhone') != -1 || useragent2.indexOf('Android') != -1 ) {
mapdivMap2.style.width = '100%';
mapdivMap2.style.height = '100%';
initialize2();
} else {
mapdivMap2.style.width = '600px';
mapdivMap2.style.height = '100%';
initialize2();
}
};
function initialize2() {
var mapOptions2 = {
zoom: 8,
center: new google.maps.LatLng(43.464258,-80.52041),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map2 = new google.maps.Map(document.getElementById('light'),
mapOptions2);
var marker2 = new google.maps.Marker({
  position: map.getCenter(),
    map: map2,
    title: 'Waterloo'
});
// Create the DIV to hold the control and call the HomeControl() constructor
// passing in this DIV.
var Map2Div = document.createElement('div');
var Map2 = new Map2(Map2Div, map2);
Map2Div.index = 1;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(Map2Div);
}

How to create new object and store the old in array each time when button clicked? OOP JavaScript

How to each time when click "Create New Poly" button, store last object in array and create a new clean object to draw new separated polyline on the map. I'd like to keep the functionality of the old polylines. Now it is not possible to clean the object. Simplified example presented bellow.
HTML:
<button onclick="create()">Create New Poly</button>
<div id="map" style="width: 500px; height: 400px;"></div>
JS:
var map;
var listener;
var polys = [],
poly = {};
create = function() {
poly = new Poly();
if ( !! listener) google.maps.event.removeListener(listener);
listener = google.maps.event.addListener(map, 'click', function(e) {
poly.addPoint(e.latLng);
});
}
function Poly() {
this.markers = [];
this.setMap(map);
polys.push(this);
}
Poly.prototype = new google.maps.Polyline();
Poly.prototype.addPoint = function(p) {
var m = poly.createMarker(p);
poly.markers.push(m);
poly.getPath().push(p);
}
Poly.prototype.createMarker = function(p) {
var marker = new google.maps.Marker({
position: p
});
marker.setMap(this.getMap());
return marker;
}
$(function() {
map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(48.864715, 10.546875),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
});
Demo: JSFIDDLE
Even after doing what Ben Davis suggested http://jsfiddle.net/LxGmR/ it still exhibits the problem.
Which I think is due to all the Ploy objects sharing the same prototype: Poly.prototype = new google.maps.Polyline();. I think the fix is that each new Ploy object needs its own prototype google.maps.Polyline instance.
Which after testing, I have found to be true: http://jsfiddle.net/uhZFE/
Then I looked up how to do this without the wrapper function, I used the method described in the SO answer https://stackoverflow.com/a/6519265/388787 (http://javascript.crockford.com/prototypal.html was also helpful) and produced http://jsfiddle.net/YgSwF/ which is the following; it works as you requested:
var map;
var listener;
var polys = [];
create = function () {
var poly = new Poly();
if ( !! listener) google.maps.event.removeListener(listener);
listener = google.maps.event.addListener(map, 'click', function (e) {
poly.addPoint(e.latLng);
});
polys.push(poly);
}
function Poly() {
google.maps.Polyline.call(this);
this.markers = [];
this.setMap(map);
}
Poly.prototype = Object.create(google.maps.Polyline.prototype);
Poly.prototype.addPoint = function (p) {
var m = this.createMarker(p);
this.markers.push(m);
this.getPath().push(p);
}
Poly.prototype.createMarker = function (p) {
var marker = new google.maps.Marker({
position: p
});
marker.setMap(this.getMap());
return marker;
}
$(function () {
map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(48.864715, 10.546875),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
});​
I believe your problem has to do with variable scope. You should declare poly inside the create() function. Also, I think it would make more sense to do the pushing of the created object into the array within the create() function to adhere to separation of concerns.
Also, in your addPoint() function, you're referring to the global poly variable when you should be using "this".
Updated code:
var map;
var listener;
var polys = [];
create = function() {
var poly = new Poly();
if ( !! listener) google.maps.event.removeListener(listener);
listener = google.maps.event.addListener(map, 'click', function(e) {
poly.addPoint(e.latLng);
});
polys.push(poly);
}
function Poly() {
this.markers = [];
this.setMap(map);
}
Poly.prototype = new google.maps.Polyline();
Poly.prototype.addPoint = function(p) {
var m = this.createMarker(p);
this.markers.push(m);
this.getPath().push(p);
}
Poly.prototype.createMarker = function(p) {
var marker = new google.maps.Marker({
position: p
});
marker.setMap(this.getMap());
return marker;
}
$(function() {
map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(48.864715, 10.546875),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
});​

Categories

Resources