I've gone through the forums trying to search for solutions, and I've adapted my code for several problems I've come across, but this one eludes me. I'm trying to plot multiple points on a map (which works) and then draw lines between the points. Each marker contains information, such as latitude and longitude, as well as it's neighbor (the point that I want to draw the line to). I also give it the color I want the line, as different 'routes' can be in different colors. The only I can see being a problem is that I draw a line, set it on the map, and run the loop again for the next marker.
My code places the markers, places the information window for each marker, but doesn't draw the lines between the markers. Can anyone see where I've screwed up, or even if this is possible to do it in the way I'm doing it?
The reason I'm keeping the drawMap function separate is in the event users want to filter out visible data, I need to re-draw the map dynamically. So, I'm keeping the marker list globally so I can reference it even after the function has finished.
My code:
http://www.nku.edu/~sweikatam1/nkuPhysicalMapTest.html
The spot in particular with the issue is the function drawMap, which gets passed an array of objects, markerList, containing the latitude, longitude, color (for the polyline), and other bits of data.
The line placement:
for(var j = 0; j < markerList.length; j++){
if(markerList[j].name == markerList[i].neighbor){
var targetDestination = new google.maps.LatLng(markerList[j].latitude,markerList[j].longitude);
//alert(markerList[i].latitude + " " + markerList[i].longitude + " \r" + markerList[j].latitude + " " + markerList[j].longitude);
}
}
//set the pathway for the two points
var flightPlanCoordinates = [
new google.maps.LatLng(markerList[i].latitude, markerList[i].longitude),
new google.maps.LatLng(targetDestination)
];
//alert("Color: " + markerList[i].routeColor);
//set the line and color
var flightPath = new google.maps.Polyline({
path:flightPlanCoordinates,
strokeColor:markerList[i].routeColor,
strokeOpacity:2,
strokeWeight:5
});
//apply lines
flightPath.setMap(map);
The function as a whole:
function drawMap(markerList){
//alert("Starting drawMap");
//create the map
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 15,
center: new google.maps.LatLng(39.032253, -84.465015),
mapTypeId: google.maps.MapTypeId.HYBRID
});
var marker;
for (var i = 0; i <markerList.length; i++){
/*alert("markerList # i: " + markerList[i].name);
alert("Marker Data: " + "Name: " + markerList[i].name + "\r "
+ "Latitude: " + markerList[i].latitude + "\r "
+ "Longitude: " + markerList[i].longitude + "\r "
+ "Content Type: " + markerList[i].contentType + "\r "
+ "Image: " + markerList[i].image
+ "Color: " + markerList[i].routeColor);*/
var contentData = '<b>'+markerList[i].name +'</b>: '
+ markerList[i].latitude + ' x '
+ markerList[i].longitude + '<br/><p>'
+ markerList[i].contentType
+'</p><img src="' + markerList[i].image + " height=150 width=100>";
//create the marker
marker = new google.maps.Marker({
position: new google.maps.LatLng(markerList[i].latitude,markerList[i].longitude),
draggable: false,
icon: markerList[i].icon,
map:map,
content: contentData,
title:markerList[i].name
});
//find the neighbor for the current marker and set the destination coordinates
for(var j = 0; j < markerList.length; j++){
if(markerList[j].name == markerList[i].neighbor){
var targetDestination = new google.maps.LatLng(markerList[j].latitude,markerList[j].longitude);
//alert(markerList[i].latitude + " " + markerList[i].longitude + " \r" + markerList[j].latitude + " " + markerList[j].longitude);
}
}
//set the pathway for the two points
var flightPlanCoordinates = [
new google.maps.LatLng(markerList[i].latitude, markerList[i].longitude),
new google.maps.LatLng(targetDestination)
];
//alert("Color: " + markerList[i].routeColor);
//set the line and color
var flightPath = new google.maps.Polyline({
path:flightPlanCoordinates,
strokeColor:markerList[i].routeColor,
strokeOpacity:2,
strokeWeight:5
});
//apply lines
flightPath.setMap(map);
//handle open information windows
google.maps.event.addListener(marker,'click', function(){
closeInfos();
var info = new google.maps.InfoWindow({content: this.content});
info.open(map,this);
infos[0]=info;
});
//alert("Marker " + markerList[i].name + " placed.");
}
//alert("drawMap complete. Drawing lines");
<!-- DRAWING LINES COMPLETE -->
}
If anything here seems odd, confusing, or needs clarification, please let me know. Criticisms and ways of doing things better are always welcome. Thanks guys!
In defining, flightPlanCoordinates, the second LatLng was being created out of another LatLng (targetDestination). So just remove the new google.maps.LatLng portion and use the targetDestination directly. With this change, I saw a blue zigzag and a red one being drawn on the map.
var flightPlanCoordinates = [
new google.maps.LatLng(markerList[i].latitude, markerList[i].longitude),
targetDestination
];
Related
I am currently developing a web application that would alert if a systems generated marker is outside a polygon, but my function is only working in 1 polygon, it does not work in other polygon. Is there something wrong in my loop or what did i miss ?
See image here: sample image
Here is my function.
function checkInPolygon(marker) {
var infowindow = new google.maps.InfoWindow();
for (var x = 0; x < mpa_polygon.length; x++) {
var html = google.maps.geometry.poly.containsLocation(marker.getPosition(), mpa_polygon[x]) ? 'inside' : 'outside';
console.log(mpa_polygon[x]);
}
infowindow.setContent(html);
infowindow.open(map, marker);
}
Full code implementation:
function displaySalesMan() {
var names = [];
var ids = [];
var dateMPA = [];
var longlat = [];
var header = [];
var loc = [
[]
];
var newdata = [];
$.ajax({
url: "/geofencing/GeofencingAPI.php",
type: "GET",
data: {
"type": "view_salesmanDashboard_gensan_COLOR",
"filter_Date": getDate()
},
dataType: "html",
crossDomain: true,
cache: false,
success: function(response) {
var data = JSON.parse(response);
if (response == 'No records are generated on selected salesman and date!') {
alert('No records are found in selected date!');
DeleteMarkers();
} else {
console.log(data.length);
for (var x = 0; x < data.length; x++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(data[x].latitude, data[x].longitude),
map: map,
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + data[x].TERRCOLOR.substr(1)
});
>
i call the
function here with a parameter ** marker **
checkInPolygon(marker);
markers.push(marker);
google.maps.event.addListener(marker, 'click', (function(marker, x) {
return function() {
var contentString = "<div style='overflow: hidden; margin: 0 auto;'><div class='modal-header' style='text-align: center;'><div class=''><img style='height: 120px; width:120px;' alt='salesmanPic' src='img/jomar.png'></div></div>" +
"<div class='table-responsive'>" +
"<table class='table table-condensed'>" +
"<tr><td>Salesman:</td><td class='bold'>" + data[x].Salesman + "</td></tr>" +
"<tr><td>Customer:</td><td>" + data[x].Customer + "</td></tr>" +
"<tr><td>Location:</td><td>" + data[x].longitude + ' ' + data[x].latitude + "</td></tr>" +
"<tr><td>Delivery Date:</td><td>" + data[x].deliveryDate + "</td></tr>" +
"</table>" +
"</div>" +
"</div>";
infoWindow.setContent(contentString);
infoWindow.open(map, marker);
}
})(marker, x));
} //end for
} //else
} //on succjess
}) //ajax close tag
//checkerForSalesman(markers, mpa_polygon);
infoWindow = new google.maps.InfoWindow;
} //display salesman funciton
From what I understood of your code, for each marker you going to ask "is this in first polygon? yes o no" then you insert the answer in the infowindow of that marker...then going to ask for the same marker "is this in the second polygon? yes o no" and so on, always overwriting the previous answer for the previous polygon....that is why it only works for the last polygon inserted.
So the solution here would be find a way to keep separate answers for each polygon, or select which polygon are you aiming.
To be clear if a marker which is only one point, can only have 2 posibles states (true or false) and you have 3 polygons..that breaks the equation. cause for instance in the image you provided, all the markers shows good results if your reference is the red polygon...but you need to know which of the 3 polygons you are referring to.
Hope it makes sense
For now, on a button click I have it so that it takes in data from two textboxes, and uses it to
1) append tweets to a panel, and
2) drop pins on a map.
My next step is to have it so that on the button click, it geodecodes a location, and does the same thing. I feel like my jquery.click function is getting really big, and wanted to know if there was a standard way to "separate" it out to make it look prettier and more readable. Can you typically have javascript functions within a jquery file that are called upon, or what is the way to go?
Here is my current jquery file. As you can see it's very big but what happens is straight forward: searchbutton on click takes some values, and sets up a new map in that location, then I access my web server's information, append it to a panel, and also drop pins on a map.
$(function () {
$("#search-button").click(function() {
// variables for google maps
var LatValue = parseFloat($("#searchLat").val());
var LonValue = parseFloat($("#searchLon").val());
var myLatLng = {lat: LatValue, lng: LonValue};
var map = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: myLatLng
});
$.getJSON(
"http://localhost:3000/tw",
{
geoSearchWord: $("#searchme").val(),
geoSearchWordLat: $("#searchLat").val(),
geoSearchWordLon: $("#searchLon").val(),
geoSearchWordRad: $("#searchRadius").val()
}
).done(function (result) {
$("#fromTweets").empty();
console.log(result);
for (i = 0; i < result.statuses.length; i++) {
//Print out username and status
$("#fromTweets").append('<b>' + "Username: " + '</b>' + result.statuses[i].user.screen_name + '<br/>');
$("#fromTweets").append('<b>' + "Tweet: " + '</b>' + result.statuses[i].text + '<br/>');
$("#fromTweets").append('<b>' + "Created at: " + '</b>' + result.statuses[i].created_at + '<br/>');
if (result.statuses[i].geo !== null) {
//Print out the geolocation
$("#fromTweets").append('<b>' + "GeoLocation: " + '</b>' + "Lat: " + result.statuses[i].geo.coordinates[0] + " Lon: " + result.statuses[i].geo.coordinates[1] + '<br/>'+ '<br/>');
//dropping a new marker on the map for each tweet that has lat/lon values
//Multiplying by i * 0.0005 to space them out in case they are from the same gelocation while still holding
//the integrity of their location.
LatValue = parseFloat(result.statuses[i].geo.coordinates[0] + i*0.0005);
LonValue = parseFloat(result.statuses[i].geo.coordinates[1] + i*0.0005);
myLatLng = {lat: LatValue, lng: LonValue};
var newMarker = new google.maps.Marker({
position: myLatLng,
map: map,
animation: google.maps.Animation.DROP,
});
} else {
$("#fromTweets").append('<b>' + "GeoLocation: " + '</b>' + "Cannot be identified" + '<br/>' + '<br/>')
}
}
});
});
The most simple and obvious thing you can do it so split your code by extracting independent logical blocks to functions:
Just something like this:
var map;
function combineTweetsAjaxRequestData()
{
return {
geoSearchWord: $("#searchme").val(),
geoSearchWordLat: $("#searchLat").val(),
geoSearchWordLon: $("#searchLon").val(),
geoSearchWordRad: $("#searchRadius").val()
};
}
function createGMap()
{
return new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: {
lat: parseFloat($("#searchLat").val()),
lng: parseFloat($("#searchLon").val())
}
});
}
function createGMarker(coords)
{
var coordsFixed = {
lat: parseFloat(coords[0] + i * 0.0005),
lng: parseFloat(coords[1] + i * 0.0005)
};
return new google.maps.Marker({
position: coordsFixed,
map: map,
animation: google.maps.Animation.DROP,
});
}
function clearInfo() {
$("#fromTweets").empty();
}
function appendInfo(title, text)
{
$("#fromTweets").append('<b>' + title + ':</b> ' + text + '<br/>');
}
function processTweet(tw)
{
appendInfo('Username', tw.user.screen_name);
appendInfo('Tweet', tw.text);
appendInfo('Created at', tw.created_at);
if (tw.geo !== null) {
var twCoords = tw.geo.coordinates;
appendInfo('GeoLocation', "Lat: " + twCoords[0] + " Lon: " + twCoords[1]);
createGMarker(twCoords);
} else {
appendInfo('GeoLocation', "Cannot be identified")
}
}
function loadTweets() {
$.getJSON(
"http://localhost:3000/tw",
combineTweetsAjaxRequestData()
).done(function (result) {
clearInfo();
console.log(result);
result.statuses.forEach(processTweet);
});
}
$(document).ready(function () {
map = createGMap();
$("#search-button").click(function() {
loadTweets();
});
});
Now, it can be easily read as a text. Your code should be readable and understandable from the first glance. Even better, if a non-developer can read it and understand some basic concepts.
What happens when the page is loaded? We create a Google map control and load tweets
How do we load tweets? We make a AJAX request by combining request data from inputs
What happens when it is loaded? We clear out current information and process every tweet
How do we process a single tweet? We output some basic information. Then, we output geolocation if it is available. Otherwise, we output an error.
Now, if you need to add information to another source, you won't extend or modify your loadTweets method - you will extend or modify appendInfo method, because the logics of information output is encapsulated here.
I have a json object infoCentros which I use to construct the map, like this:
for ( var i = 0; i < infoCentros.length; i++ ) {
var centro = infoCentros[i];
var lat = centro.cordenadas.lat;
var lon = centro.cordenadas.long;
if (lat && lon) {
c++;
latlon = new google.maps.LatLng(lat, lon);
var moptions = {
position: latlon,
map: $project.gmap
}
moptions.icon = theme_uri + '/images/marker.png';
var marker = new google.maps.Marker(moptions);
$project.mapMarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
$project.mapInfoWindow.setContent(
'<div class="sescam-info-window">' +
'<h3>' + centro.nombre + '</h3>' +
'<p>' + centro.lugar + '</h3>' +
'<p>Coordinador</p>' +
'<p>' + centro.coordinador.nombre + '</p>' +
'<p>' + centro.coordinador.email + '</p>' +
'<p>Responsable</p>' +
'<p>' + centro.responsable.nombre + '</p>' +
'<p>' + centro.responsable.email + '</p>'
+ '</div>'
);
$project.mapInfoWindow.open($project.gmap, marker);
});
$project.mapBounds.extend(latlon);
}
}
It seems to work fine, but if I have 5 markers, It doesn't matter which one I click, the infowindow always corresponds to the last item (position and content),
Any idea what am I missing? I thought that passing marker to the addListener would Do the trick..
Problem is with third argument of google.maps.event.addListener. That anonymous function contains the variable from the parent scope which when evaluated with marker click will always take the last value assigned. Look for closure for more details. However you may able to get the desired behaviour by using "bind" feature of javascript function prototype in below manner:
//inside for loop
google.maps.event.addListener(marker, 'click', handleMarkerClick.bind(undefined, marker, i));
//other codes if any...
defining handleClick
function handleMarkerClick(marker, index) {
if (typeof infowindow === 'undefined') {
infowindow = new google.maps.InfoWindow({});
}
var data = infoCentros[index]//helpful data
//create content with dynamic data
infowindow.setContent("dynamic content");
infowindow.open(marker.getMap(), marker);//modify as per your requirement
}
Duncan is right, he means you have to attach the listener outside of your for loop. It's not specific to Google Maps, it's how javascript works. Try searching for "javascript closures for loop" or visit this link - Closures in Javascript for a simple explanation.
I'm trying to create a group of markers with info box attached. However, no matter which marker I click, it always opens the info box of the last detailmarker. Anyone know why? Please help.
var stepDisplay = new google.maps.InfoWindow();
function AddDetailMarker(map, itinerary) {
var markers = [];
for (var i = 1; i < itinerary.Legs.length; i++) {
var position = new google.maps.LatLng(itinerary.Legs[i].BusStop.Latitude, itinerary.Legs[i].BusStop.Longitude);
var title = itinerary.Legs[i].BusStop.Code + ": " + itinerary.Legs[i].BusStop.Location + " " + itinerary.Legs[i].BusStop.Street + ", Quận " + itinerary.Legs[i].BusStop.Ward;
var detailmarker = new google.maps.Marker({
position: position,
map: map,
title: title,
icon: "/Content/img/customized_marker/" + "blue" + "/" + "bus-stop2" + ".png"
});
google.maps.event.addListener(detailmarker, 'click', function () {
stepDisplay.setContent(title);
stepDisplay.open(map, detailmarker);
});
markers[i-1] = detailmarker;
}
}
Edit: possible dublicate of Google maps infowindow showing on wrong marker. I've tried all the solutions I found here and none works.
Yes, this is exactly the same problem as the other one you linked to, and the solution for your code is the same - put the code to create each marker into a function, and call that function in your loop:
var stepDisplay = new google.maps.InfoWindow();
function AddDetailMarker(map, itinerary) {
for (var i = 1; i < itinerary.Legs.length; i++) {
addLegMarker( map, itinerary.Legs[i] );
}
}
function addLegMarker( map, leg ) {
var position = new google.maps.LatLng(leg.BusStop.Latitude, leg.BusStop.Longitude);
var title = leg.BusStop.Code + ": " + leg.BusStop.Location + " " + leg.BusStop.Street + ", Quận " + leg.BusStop.Ward;
var detailmarker = new google.maps.Marker({
position: position,
map: map,
title: title,
icon: "/Content/img/customized_marker/" + "blue" + "/" + "bus-stop2" + ".png"
});
google.maps.event.addListener(detailmarker, 'click', function () {
stepDisplay.setContent(title);
stepDisplay.open(map, detailmarker);
});
}
Do you see why that fixes it? The title and detailmarker are now specific to each invocation of addLegMarker(). In the original code, there was only a single copy of each of these variables, shared among all markers.
I am wanting to search a KML file to see if a particular address falls within an overlay. Presently, I have the address converting to a geocode. However, I'm not sure what code is needed to add this functionality.
Here's the present code:
function initialize() {
var infowindow = new google.maps.InfoWindow({
content: '',
suppressMapPan:true
});
var myLatlng = new google.maps.LatLng(35.910200,-84.085100);
var myOptions = {
zoom: 12,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(
document.getElementById("map_canvas"), myOptions);
var d = new Date();
var n = d.getMilliseconds();
var nyLayer = new google.maps.KmlLayer(
'http://www.cspc.net/neighborhoods/groups.kml?rand=' + n,
{ suppressInfoWindows: true, map: map});
google.maps.event.addListener(nyLayer, 'click', function(kmlEvent) {
var url = kmlEvent.featureData.snippet;
var groupName = kmlEvent.featureData.name;
var insideContent = "<div style='width:250px;'><h2>" + groupName +
"</h1><p>We have a neighborhood contact in your area! </p>" +
"<p><a href='" + url + "' target='_blank'>Get connected!</a>" +
" They look forward to hearing from you.</p><p>If you have " +
"any additional questions, please contact our " +
"<a href='http://www.cspc.net/communitylife' target='_blank'>" +
"Community Life</a> staff for more information. Betsy Palk, " +
"the Administrative Assistant, may be reached at:<br/><br/>" +
"<b>Email:</b> <a href='mailto:betsypalk#cspc.net'>" +
"betsypalk#cspc.net</a><br/><b>Phone:</b> 865-291-5268<p></div>";
var clickPos = kmlEvent.latLng;
var posX = new google.maps.LatLng(clickPos.lat(), clickPos.lng());
infowindow.close();
infowindow.setPosition(posX);
infowindow.setContent(insideContent);
infowindow.open(map);
});
eventMapClick = google.maps.event.addListener(map, 'click',
function(event) {
var marker = new google.maps.Marker({ position: event.latLng });
var outsideContent = "<div style='width:250px;'><h2>Oops!</h1>" +
"<p> It seems we don't have a neighborhood contact in your " +
"area.</p><p>Please contact our <a " +
"href='http://www.cspc.net/communitylife' target= '_blank'>" +
"Community Life</a> staff for more information. " +
"Betsy Palk, the Administrative Assistant, may be reached at:" +
"<br/><br/><b>Email: </b> <a href='mailto:betsypalk#cspc.net'>" +
"betsypalk#cspc.net</a><br/><b>Phone:</b> 865-291-5268<p></div>";
infowindow.setContent(outsideContent);
infowindow.open(map, marker);
});
}
var geocoder = new google.maps.Geocoder();
function searchAddress(address) {
geocoder.geocode(
{'address': address},
function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var loc = results[0].geometry.location;
// use loc.lat(), loc.lng()
window.alert(loc);
}
else {
window.alert("Not found: " + status);
}
}
);
};
If I understand your question, I believe you want the formula to determine if a point (google.maps.LatLng) falls within one of your KML Placemark definitions (that you give names such as: Neighborhood Group 1). Within each Placemark, you define a Polygon and within each Polygon, you define a set of coordinates, which represent the vertices of the Polygon.
Using the coordinates within the Polygon and the LatLng you retrieve via geocoding, you could start with these formulas and select that one that is the best fit for you:
Fast Winding Number Inclusion of a Point in a Polygon
Point-In-Polygon Algorithm
A very similar question has also been asked here on SO: Determine the lat lngs of markers within a polygon.