I've got a 3 pane google map integration in our VB.net software. For the most part it works brilliantly. Today I'm trying to get these 3 seperate objects to paint different polygons depending on the lat/lngs they're sent for each day.
First map control works all of the time. 2nd and 3rd however are being troublesome. I've attempted to screenshot what's going on.
As you can see, the polys are being drawn, but for some reason an entirely new map is being drawn on top of my original. Obviously this isn't right. I've got code all over the place too:
Here's the JS initialize that is called upon DocumentComplete firing:
function Initialize(zoomLevel,lat,lng,type, bCanEdit, bCanDrag){
//Get the type of map to start.
//Need to convert the GoogleMapType enum
//to an actual Google Map Type
var MapType;
switch (type)
{
case 1:
MapType = google.maps.MapTypeId.ROADMAP;
break;
case 2:
MapType = google.maps.MapTypeId.TERRAIN;
break;
case 3:
MapType = google.maps.MapTypeId.HYBRID;
break;
case 4:
MapType = google.maps.MapTypeId.SATELLITE;
break;
default:
MapType = google.maps.MapTypeId.ROADMAP;
};
//Create an instance of the map with the lat, lng, zoom, and
//type passed in
myLatlng = new google.maps.LatLng(lat,lng);
var myOptions = {zoom: zoomLevel,center: myLatlng,mapTypeId: MapType};
var MarkerSize = new google.maps.Size(48,48);
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
google.maps.event.addListener(map, 'click', Map_Click);
google.maps.event.addListener(map, 'mousemove', Map_MouseMove);
google.maps.event.addListener(map, 'idle',Map_Idle);
overlayLayer = new Array();
overlayCount = 0;
driverRouteLayers = new Array();
driverRouteCount = 0;
canEdit = bCanEdit;
canDrag = bCanDrag;
}
The VB.net that invokes the script to draw the overlay areas:
Public Sub DrawAreaOverlays(ByVal area As DeliveryArea)
Dim gc As New GComArray()
For Each p As DeliveryAreaPoint In area.Points
Dim ll As New GLatLong(p.Latitude, p.Longitude)
gc.Add(ll)
Next
WebBrowser1.Document.InvokeScript("DrawAreaOverlay", {gc, Utility.Pens.GetHexColor(area.Colour)})
End Sub
And the initial DocumentComplete hook-in in vb.net:
Private Sub WebBrowser1_DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs)
'Initialize the google map with the initial settings.
'The Initialize script function takes four parameters.
'zoom, lat, lng, maptype. Call the script passing the
'parameters in.
WebBrowser1.Document.InvokeScript("Initialize", New Object() {InitialZoom, InitialLatitude, InitialLongitude, CInt(InitialMapType),
allowEditing, allowDragging})
RaiseEvent PopulateInitialRoutes()
End Sub
These are the only methods that are called to start with, the rest are just hooks into the javascript methods to display various things, but nothing is called (like this) prior to this point. And on this specific form, I'm only displaying, not allowing end-users to manipulate the data in any way.
The DrawAreaOverlay method (JS) is responsible for the poly drawing (please excuse the state of the code, I've been ripping it apart trying to figure this out!):
function DrawAreaOverlay(area, col)
{
if(area.Count <= 0)
{
CreateNewArea(0, 0);
return;
}
var coordsString = "";
var areaCoords = [];
overlayLayer[overlayCount] = new Array();
for(var j=0; j<area.Count; j++)
{
areaCoords.push(new google.maps.LatLng(area.Item(j).lat, area.Item(j).lng));
}
var poly = new google.maps.Polygon({
paths: areaCoords,
strokeColor: '#'+col,
strokOpacity: 0.35,
strokeWeight: 2,
fillColor: '#'+col,
fillOpacity: 0.25,
geodesic: false,
editable: canEdit,
draggable: canDrag,
map: map
});
poly.getPaths().forEach(function(path, index){
google.maps.event.addListener(path, 'set_at', function(){
var arrayOfPoints = new Array();
arrayOfPoints = poly.getPath();
coordsString = "";
for(var i=0; i<arrayOfPoints.length; i++)
{
coordsString += poly.getPath().getAt(i).lat() + ", " + poly.getPath().getAt(i).lng() + "|";
}
window.external.AreaPointMoved(coordsString.substring(0, coordsString.length -1));
});
});
// INSERTION OF NEW VERTICES ALONG THE POLYGON EDGES //
poly.getPaths().forEach(function(path, index){
google.maps.event.addListener(path, 'insert_at', function(){
var arrayOfPoints = new Array();
arrayOfPoints = poly.getPath();
coordsString = "";
for(var i=0; i<arrayOfPoints.length; i++)
{
coordsString += poly.getPath().getAt(i).lat() + ", " + poly.getPath().getAt(i).lng() + "|";
}
window.external.AreaPointMoved(coordsString.substring(0, coordsString.length -1));
});
});
debugger;
//if(map == null)
//{
// makeMeANotNullMap(zoomLevel, latitude, longitude);
// overlayLayer[overlayCount] = new Array();
//}
poly.setMap(map);
overlayLayer[overlayCount].push(poly);
overlayCount++;
//alert("I'll work now, but why?");
//window.external.DoNothing();
}
Now the REALLY weird part. If I uncomment the alert box in JS (the: I'll work now, but why? alert) each map renders wonderfully. So something clearly happens to the map once the alert box has been dismissed. It's this missing link I'm after. But I can't fathom it.
Without the alert box, interestingly, the 2nd and 3rd initialisations of the map controls, map is sometimes null. I know this, because I've setup VS to debug the script. So you can drag each one (vigorously) and get the attached image above outcome, where the poly has drawn, but you can't see it.
Any help is greatly appreciated. I don't have a clue why this is happening.
Related
I'm getting back an array of JavaScript objects from a database. I'm looping through that array and creating objects to be displayed on the screen. I'm trying to use .addEventListener() to each object but I just realized that addEventListener() only works for DOM objects.
How could I add an event listener to each JavaScript object?
Here is my code:
var objects = JSON.parse(data.responseText);
var i;
var objectLn = objects.length;
for (i = 0; i < objectLn; i++) {
//Puts each object into a variable
var eachObject = objects[i];
//Establishes pin position
var pinPos = {
lat: parseFloat(eachObject.lat),
lng: parseFloat(eachObject.lng)
};
//Creates new icon for pin
var icon = {
url: "path/to/img",
scaledSize: new google.maps.Size(60, 60),
origin: null,
anchor: null
};
//Creates a new pin from pulled information
var pin = new google.maps.Marker({
position: pinPos,
map: map,
icon: icon
});
//Alerts each object on click
pin.addListener('click', function() {
alert(JSON.stringify(eachObject));
map.panTo(pinPos);
map.setCenter(pinPos);
});
}
addListener is adding a 'click' listener to each pin that is dropped, so that's good. The problem is that it's supposed to alert each object, but it's only alerting the last object I retrieve from the database on every pin I click.
try doing your loopage like so..
for (i = 0; i < objectLn; i++) {
(function(eachObject){
// copy the loop contents here. eachObject is already defined.
})(objects[i]);
}
Simply put, I am trying to dynamically create a marker and corresponding label for each property of in the loop. The label will contain the time-stamp.
However, I am fairly stuck on figuring out how to get the on-click function to remain bound to it's respective marker. The infowindow variable as well.
I have tried using a counter and creating an array of variables to reference each function. I have also tried eval and creating new variables within the global window
window["foo" + counter]
unsuccessfully. With all variations of this that I have tried the code has broken or resulted in all triggers/infowindows being bound to the same property.
Any help or direction would be greatly appreciated. I think I am just missing some depth on understanding variable/object scope. This base code results in all markers appearing and the final label (since the previous ones are overwritten) as depicted below.
variables:
prop = string representing long,lat coordinates
obj[prop] = An ISOformat timestamp
Rest of the code taken from marker Labels Google Maps API
for (var prop in obj) {
// skip loop if the property is from prototype
if(!obj.hasOwnProperty(prop)) continue;
var templocs = prop.split(",").map(Number);
console.log(templocs[0]);
var temppos = new google.maps.LatLng(templocs[0], templocs[1]);
var mark = new google.maps.Marker({
position:temppos,
map: map,
animation:google.maps.Animation.BOUNCE,
});
var infowindow = new google.maps.InfoWindow({
content: "date taken: " + obj[prop],
maxWidth: 200
});
mark.addListener('click', function() {
infowindow.open(map, mark);
});
}
var cnt = 0;
var infowindow = [];
var mark = [];
var bounds = new google.maps.LatLngBounds();
for (var prop in obj) {
// skip loop if the property is from prototype
if(!obj.hasOwnProperty(prop)) continue;
var templocs = prop.split(",").map(Number);
console.log(templocs[0]);
var temppos = new google.maps.LatLng(templocs[0], templocs[1]);
infowindow[cnt] = new google.maps.InfoWindow({
content: "date taken: " + obj[prop],
maxWidth: 200
});
mark[cnt] = new google.maps.Marker({
position: temppos,
map: map,
animation:google.maps.Animation.BOUNCE,
});
/* mark[cnt].addListener('click', function() {
infowindow[cnt].open(map, mark);
}); */
google.maps.event.addListener(mark[cnt], 'click', (function(markerrr, cnt) {
return function() {
infowindow[cnt].open(map, mark[cnt]);
}
})(mark[cnt], cnt));
bounds.extend(mark[cnt].getPosition());
cnt++;
}
Fixed. This post is a duplicate of this one
Issue had to do with closure.
When I push a marker to the array it plots the marker correctly and adds a title to the array too.
I know this works because when I console.log the console.log(markersArray); it returns the following. the title of the marker is next door.
The below is my marker click that opens up the info window, but when I console.log out the data which is called mll it doesn't have the title inside it.
google.maps.event.addListener(marker, 'click', function(mll) {
console.log(mll);
var html= "<div style='color:#000;background-color:#fff;padding:5px;width:150px;'><p>"+mll+"</p></div>";
iw = new google.maps.InfoWindow({content:html});
iw.open(map,marker);
});
How would I be able to get the click function to pull in the title when the array has it and has pushed it successfully?
I remember doing something similar before. In my case, I used infoWindows with circle markers. Essentially, I had both in separate arrays. When I made the circle marker, I gave it a unique value, called place, which was basically it's count (the value of the n-th circle created, was n). On the event listener, I called the infoWindow from the other array based on the position of the current circle.
You can make an array var titles = []; to hold titles.
Each time you make a new marker, increment a var count = 0;, keeping track of how many markers you have.
In your marker options, add place: count. When you need a specific title, you can call titles[marker.place];
var infos = [];
var count = 0;
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var lastWindow;
count++;
var populationOptions = {
//leaving out defaults
place: count
};
var circle = new google.maps.Circle(populationOptions);
lastCircle = circle;
var contentString = 'just a string...';
var infowindow = new google.maps.InfoWindow({
content: contentString,
position: new google.maps.LatLng(data.lon, data.lad)
});
infos.push(infowindow);
google.maps.event.addListener(circle, 'mouseover', function() {
if(lastWindow){
lastWindow.close();
}
infos[circle.place].open(map);
lastWindow = infos[circle.place];
});
I am developing am app that shows a list of different places using google maps, i have everything working fine and working with currentLocation and the closestsLocation and it zooms to show this, but I have tried to change this to set it so that it zooms out to show all the buildings and not just the closest one
EDIT: this is the working code
// Sets the bounds of the map to include at lease one visable marker
function setBounds(){
if ( markerCluster.getMarkers().length > 0 && homeMarker != null){
map.setZoom(17);
var visableMarkers = markerCluster.getMarkers();
// want to set the starting position at the homeMarker
//Make an array of the LatLng's of the markers you want to show
var LatLngList = new Array ();
LatLngList.push(homeMarker.position);
$.each(visableMarkers, function() {
LatLngList.push(this.position);
});
// Create a new viewpoint bound
var bounds = new google.maps.LatLngBounds ();
// Go through each...
for (var i = 0, LtLgLen = LatLngList.length; i < LtLgLen; i++) {
// And increase the bounds to take this point
bounds.extend (LatLngList[i]);
}
// Fit these bounds to the map
map.fitBounds (bounds);
}
}
The code for your array LatLngList is incorrect, I think it should say
var LatLngList = new Array (new google.maps.LatLng (52.537,-2.061), new google.maps.LatLng (52.564,-2.017));
I have a google maps api function place markers which I'm using from the tutorial found here:Google Maps API with JQuery
By any means, I had to modify the javascript to account for my application. I'm pulling markers from an XML file like before, though this time I'm getting multiple requests, and multiple standard deviation, time to serve, and means for these requests. I've set up the XML to have these with a counter appended to the tag, but it looks like it's not rendering into an array correctly.
To note, I've never used Javascript, and am mostly flying by the seat of my pants on this, so if it's an atrocity of Javascript, feel free to let me know, the entire generation of the XML is in Python.
Sample of the XML: (I apologize, I don't know how to show < or > on stack overflow without it simply hiding it as a tag. Around each "markers" "marker" "name" "requestX" "timetoserveX" etc. is the < and > for tags in XML.
markers
marker
name Simpletown, CA /name
request0 /resource/ /request0
timetoserve0 .001 Seconds to serve request /timetoserve0
mean0 .5309 Mean in seconds /mean0
std_dev0 .552 Standard Deviation in Seconds /std_dev0
request1 /resource2/ /request1
timetoserve1 0.015626 Seconds to serve request /timetoserve1
mean1 0.0011 Mean in seconds /mean1
std_dev1 0.004465 Standard Deviation in Seconds /std_dev1
/marker
/markers
MYMAP.placeMarkers = function(filename) {
$.get(filename, function(xml){
$(xml).find("marker").each(function(){
var name = $(this).find('name').text();
var count = 0;
var requeststring = 'request' + Integer.toString(count)
var request = new Array();
var timetoserve = new Array();
var mean = new Array();
var std_dev = new Array();
var timetoservestring = 'timetoserve' + Integer.toString(count);
var meanstring = 'mean' + Integer.toString(count);
var std_devstring = 'std_dev' + Integer.toString(count);
while ($(this).find(requeststring).text()){
timetoservestring = 'timetoserve' + Integer.toString(count);
meanstring = 'mean' + Integer.toString(count);
std_devstring = 'std_dev' + Integer.toString(count);
request[count] = $(this).find(requeststring).text();
timetoserve[count] = $(this).find(timetoservestring).text();
mean[count] = $(this).find(meanstring).text();
std_dev[count] = $(this).find(std_devstring).text();
count++;
requeststring = 'request' + Integer.toString(count)
}
// create a new LatLng point for the marker
var lat = $(this).find('lat').text();
var lng = $(this).find('lng').text();
var point = new google.maps.LatLng(parseFloat(lat),parseFloat(lng));
// extend the bounds to include the new point
MYMAP.bounds.extend(point);
var marker = new google.maps.Marker({
position: point,
map: MYMAP.map
});
var infoWindow = new google.maps.InfoWindow();
var html = ""
for (i=0;i<count;i++){
html=html+'<strong>'+name+'</strong.><br />'+request[i]+'<br />'+timetoserve[i]+'<br />'+mean[i]+'<br />'+std_dev[i]+<br />;
//var html='<strong>'+name+'</strong.><br />'+request+'</strong.><br />'+timetoserve+'</strong.><br />'+mean+'</strong.><br />'+std_dev;
}
google.maps.event.addListener(marker, 'click', function() {
infoWindow.setContent(html);
infoWindow.open(MYMAP.map, marker);
});
MYMAP.map.fitBounds(MYMAP.bounds);
});
});
}
With the updates, it's not longer having the array issues it looks like, even though I am getting the map to render at this point. The "Show Markers" button is not populating the map with markers. Running FireBug with this seems to only spew endless amounts of "Break on error" hits and warnings for jQuery.
var request[count] = $(this).find(requeststring).text();
var timetoserve[count] = $(this).find(timetoservestring).text();
var mean[count] = $(this).find(meanstring).text();
var std_dev[count] = $(this).find(std_devstring).text();
to
request[count] = $(this).find(requeststring).text();
timetoserve[count] = $(this).find(timetoservestring).text();
mean[count] = $(this).find(meanstring).text();
std_dev[count] = $(this).find(std_devstring).text();
-assuming these are the arrays that are not working correctly for you.
If I may make a suggestion (RE your comment this morning):
while ($(this).find(requeststring).text()){
//...omitted
}
if you were to rename all your tags to the same thing and give them an id attribute (which is valid in xml) you can do this:
var requests = $(this).find('request');
var timetoserves = $(this).find('timetoserve');
// etc...
for (var i=0; i<requests.length; i++) {
request[i] = requests.eq(i).text();
timetoserve[count] = timetoserves.eq(i).text();
//etc...
}
which would probably provide better performance.