Google map : How to create a custom infowindow used custom overlay - javascript

i'm a newbie in javascript
use custom overlay but always detect 'cannot read property 'setContent' of undefined'
my javascript code is https://github.com/SaneMethod/CGWin/blob/master/src/cGWin.js
and i use jquery because of parsing Exel file
////https://github.com/SaneMethod/CGWin/blob/master/src/cGWin.js/////
function GenCustomWindow () {
var CustomWindow = function () {
....
}
}
////parsing code////
$(document).ready(function () {
$.ajax({
type: "GET",
url: "",
datatype: "text",
success: function (data) { processData(data); }
});
});
function processData(allText) {
....
var info = new GenCustomWindow();
for(i = 0;i < name.length;i++)
{
marker = new google.maps.Marker({
position: new google.maps.LatLng(a, b),
map: map,
icon: markerImage,
optimized: false
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
content =
'<div class="iw-title">' +
name[i] + '</div>' +
'<div class="iw-content">' +
'<div class="iw-subTitle">' + add[i] + '</div>' +
'</div>' +
'<div class="iw-bottom-gradient"></div>' +
'</div>';
info.CustomWindow.setContent('content');
}
})(marker, i));
}
}
there is always error in info.CustomWindow.setContent
why is this code an error?
and can you recommend another custom infowindow?

GenCustomWindow() returns a CustomWindow, which means info is alread a CustomWindow.
Change
info.CustomWindow.setContent('content');
to
info.setContent('content');
And everything should work fine.

Related

JavaScript for loop inside async function

I am running into promise error inside for loop which is inside async function.
var circles=[];
async function displayMarkersOnGoogleMap(locations) {
try {
for (i = 0; i < locations.length; i++) {
var latlong = new google.maps.LatLng(locations[i].latitude, locations[i].longitude);
var marker = new google.maps.Marker({
position: latlong,
title: ""
icon: {
url: "https://maps.google.com/mapfiles/ms/icons/red-dot.png",
labelOrigin: { x: 12, y: -10 }
},
map: map,
label: {
text: "",
color: "red",
fontWeight: "bold",
fontsize:"16px"
}
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
console.warn("Business Marker clicked");
var distanceInMeters = google.maps.geometry.spherical.computeDistanceBetween(
currentPosMarker.getPosition(),
marker.getPosition()
);
try {
circles.forEach(async (circle) => {
await circle.setMap(null);
})
} catch (e) {
}
if (i == undefined)
i = 0;
setTimeout(() => {
var circle = drawCircle(map, latlong, locations[i].rangeLimitInMeters);
circles.push(circle);
}, 5000);
var content = "<span style='font-weight: bold;'>" + locations[i].locationName + "</span>"
content = content + "<br/> " + locations[i].address + "<br/> " + locations[i].city + ", " + locations[i].state;
if (locations[i].locationOpenStatus == "CLOSE") {
content = content + "<br/><span class='badge badge-danger'>" + locations[i].locationOpenStatus + "</span>";
}
else {
content = content + "<br/><span class='badge badge-success'>" + locations[i].locationOpenStatus + "</span>";
}
content = content + "<br/><span style='font-weight: bold;'> Time : </span> " + locations[i].locationStartTime + " To " + locations[i].locationStopTime;
infowindow.marker = marker;
infowindow.setContent(content);
infowindow.open(map, marker);
}
})(marker, i));
markersArrray.push(marker);
}
} catch (e) {
console.error("Javascript:displayMarkersOnGoogleMap:-" + e.errorMessage );
}
}
How do I get around this ?
All I am trying to do is clear all the previous Circles that I might have drawn , before drawing the new one.
circles.forEach(async (circle) => {
await circle.setMap(null);
})
This right here, wont work as you expect it to. forEach loop doesn't work with async callbacks. Even if you explicitly mark your callback function as async, forEach loop wont wait for it to complete the promise.
Instead try with a for...of loop.This would execute whatever is inside synchronously, in the order you normally read the code.
for(const circle of circles) {
await circle.setMap(null);
}

ESRI WAB custom widget popup disappears after 1 second

I am working on an in-panel custom widget for our WebApp on ESRI. When the widget is active the map is listening to mouse click events. When fired, a REST service is called via ajax and the result will be displayed in a popup.
If there is no other layer like WMS/WFS active everything is working fine. But with another active layer, the popup appears for a second and then disappears.
Any idea?
define(['dojo/_base/declare', 'jimu/BaseWidget', 'esri/layers/layer', 'dojo/dom-construct', 'esri/geometry/webMercatorUtils', 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'],
function(declare, BaseWidget, Layer, domConstruct, webMercatorUtils) {
var map;
return declare([BaseWidget], {
baseClass: 'jimu-widget-myTest',
name: 'myTest',
customPopup: new Popup({
offsetX: 10,
offsetY: 10,
visibleWhenEmpty: true
},domConstruct.create("div")),
startup: function() {
map = this.map;
this.map._mapParams.infoWindow = this._customPopup;
},
onOpen: function(){
document.getElementById(this.map.id).addEventListener("click", myClickListener);
},
onClose: function(){
document.getElementById(this.map.id).removeEventListener("click", myClickListener);
}
});
function myClickListener(evt) {
if(evt.mapPoint) {
var content = "";
var mp = webMercatorUtils.webMercatorToGeographic(evt.mapPoint);
lon = mp.x;
lat = mp.y;
var service_url = "xxxxxx";
$.ajax({
method: 'GET',
url: service_url,
dataType: 'json',
error: function() {
console.log("Could not load data.");
},
success: function(result) {
for(var i = 0; i < result.values.length; i++) {
content += "<div class='test'>";
content += "<table style='width:100%'><tr><td><b>bli</b></td><td>" + result.values[i].bli + "</td></tr>" +
"<tr><td><b>bla</b></td><td>" + result.values[i].bla + "</td></tr>" +
"<tr><td><b>blub</b></td><td>" + result.values[i].blub + "</td></tr>";
content += "</table></div>";
}
map.infoWindow.setTitle("myTest");
map.infoWindow.setContent(content);
map.infoWindow.show(evt.screenPoint);
}
});
}
}
});
Problem solved by adding the Listener this way
onOpen: function(){
map.on("click", myClickListener);
},
and not like this
onOpen: function(){
document.getElementById(this.map.id).addEventListener("click", myClickListener);
},

Google Map Geocoded TypeError in Callback function

I have the following 2 functions to pull in, geocode, and place markers in a google map.
I keep getting a TypeError: adds[i] is undefined, which of course is causing the rest of the map to bomb.
Here is my code:
// Place Markers on the Map
var PlaceMarkers = function (iw, adds, gc) {
var image = {url: "http://meatmysite.com/Images/star2.png", size: new google.maps.Size(24, 24)};
var aCt = adds.length;
for(var i = 0; i < aCt; ++i) {
GetLatLng(gc, adds[i].address, function(pos) {
if(pos) {
var ipop = '<h1>' + adds[i].title + '</h1>'; // <----- TypeError: adds[i] is undefined
if(!isBlank(adds[i].url)){
ipop += '' + adds[i].url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + adds[i].content + '</div>';
if(!isBlank(adds[i].mainphone)){
ipop += '<br /><strong>Phone:</strong> ' + adds[i].mainphone + '';
}
if(!isBlank(adds[i].mainemail)){
ipop += '<br /><strong>Email:</strong> ' + adds[i].mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({title: adds[i].title, position: pos, map: map, icon: image, html: ipop});
google.maps.event.addListener(mark, 'click', function(){
iw.setContent(this.html);
iw.open(map, this);
});
}
});
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
var ret = '';
gc.geocode({'address': add}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location);
console.log('Found Here: ' + ret.toString());
}
});
return -1;
};
DEMO RETURNED DATA FOR adds
[
{
"address": "1 My Street Gilbert, AZ 85234",
"title": "My Title 1",
"url": "http://www.myurl.com/",
"mainphone": null,
"mainemail": null,
"content": "1 My Street<br />Gilbert, AZ 85234"
},
{
"address": "2 My Street North Richland Hills, TX 76182",
"title": "My Title 2",
"url": null,
"mainphone": null,
"mainemail": null,
"content": "2 My Street<br />North Richland Hills, TX 76182"
}
]
One option, pass the complete "address" object into the GetLatLng function, and from there into its callback (so you get function closure on it):
// Get Lat/Lng Location
var GetLatLng = function (gc, add, f) {
gc.geocode({
'address': add.address
}, function (res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
}
});
};
Then use it like this inside the callback (you could pass just the index into the array also):
GetLatLng(gc, adds[i], function (pos, add) {
if (pos) {
var ipop = '<h1>' + add.title + '</h1>';
if (!isBlank(add.url)) {
ipop += '' + add.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + add.content + '</div>';
if (!isBlank(add.mainphone)) {
ipop += '<br /><strong>Phone:</strong> ' + add.mainphone + '';
}
if (!isBlank(add.mainemail)) {
ipop += '<br /><strong>Email:</strong> ' + add.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({
title: add.title,
position: pos,
map: map,
icon: image,
html: ipop
});
google.maps.event.addListener(mark, 'click', function () {
iw.setContent(this.html);
iw.open(map, this);
});
}
});
proof of concept fiddle
code snippet:
var geocoder = new google.maps.Geocoder();
var map;
var infoWindow = new google.maps.InfoWindow();
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
});
PlaceMarkers(infoWindow, adds, geocoder);
}
google.maps.event.addDomListener(window, "load", initialize);
// Place Markers on the Map
var PlaceMarkers = function(iw, adds, gc) {
var bounds = new google.maps.LatLngBounds();
var image = {
url: "http://meatmysite.com/Images/star2.png",
size: new google.maps.Size(24, 24)
};
var aCt = adds.length;
for (var i = 0; i < aCt; ++i) {
GetLatLng(gc, adds[i], function(pos, add) {
if (pos) {
var ipop = '<h1>' + add.title + '</h1>'; // <----- TypeError: adds[i] is undefined
if (!isBlank(add.url)) {
ipop += '' + add.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + add.content + '</div>';
if (!isBlank(add.mainphone)) {
ipop += '<br /><strong>Phone:</strong> ' + add.mainphone + '';
}
if (!isBlank(add.mainemail)) {
ipop += '<br /><strong>Email:</strong> ' + add.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({
title: add.title,
position: pos,
map: map,
// icon: image,
html: ipop
});
bounds.extend(mark.getPosition());
map.fitBounds(bounds);
google.maps.event.addListener(mark, 'click', function() {
iw.setContent(this.html);
iw.open(map, this);
});
}
});
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
gc.geocode({
'address': add.address
}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
}
});
};
var adds = [{
"address": "1 My Street Gilbert, AZ 85234",
"title": "My Title 1",
"url": "http://www.myurl.com/",
"mainphone": null,
"mainemail": null,
"content": "1 My Street<br />Gilbert, AZ 85234"
}, {
"address": "2 My Street North Richland Hills, TX 76182",
"title": "My Title 2",
"url": null,
"mainphone": null,
"mainemail": null,
"content": "2 My Street<br />North Richland Hills, TX 76182"
}];
function isBlank(str) {
return (!str || /^\s*$/.test(str));
}
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas"></div>
This looks like a typical binding issue. By the time your callback is called, the value of adds[i] will have changed. It is likely that the loop terminated and i has now a value of last index + 1, which is pointing to nothing. Note that it could also point to the wrong index, that would not fail but use the wrong data.
You must bind the value of adds[i] locally for each iteration or the callback will just use a reference to a global value. There a multiple ways to go about this, here is a simple one where we keep passing adds[i] along as a function argument.
Replace adds[i].address with adds[i] when calling GetLatLng and add a second parameter add to the callback:
GetLatLng(gc, adds[i], function(pos, add) {
...
});
Then modify GetLatLng to use add.address instead of just add and add add to the callback call:
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
var ret = '';
gc.geocode({'address': add.address}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
console.log('Found Here: ' + ret.toString());
}
});
return -1;
};
Then in the callback function, replace all instances of adds[i] with add to use the local variable.
I didn't set up a test but it should theoretically work.
you appear to be overcomplicating things. Any reason why you can't do this?
// Place Markers on the Map
var PlaceMarkers = function (iw, adds, gc) {
var aCt = adds.length;
for(var i = 0; i < aCt; ++i) {
var obj=adds[i];
GetLatLng(gc, obj)
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, obj) {
var ret = '';
gc.geocode({'address': obj.address}, function(res, status) {
if (status == 'OK') {
var pos=res[0].geometry.location;
var ipop = '<h1>' + obj.title + '</h1>'; // <----- TypeError: adds[i] is undefined
if(!isBlank(obj.url)){
ipop += '' + obj.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content">' + obj.content + '</div>';
if(!isBlank(obj.mainphone)){
ipop += '<br /><strong>Phone:</strong> ' + obj.mainphone + '';
}
if(!isBlank(obj.mainemail)){
ipop += '<br /><strong>Email:</strong> ' + obj.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({title: obj.title, position: pos, map: map, html: ipop});
google.maps.event.addListener(mark, 'click', function(){
iw.setContent(this.html);
iw.open(map, this);
});
} else {
console.log("geocoder problem!")
}
});
};
for(var i = 0; i < aCt - 1; ++i). You need to add "-1" in you for-loop. The array starts at index 0 and not 1. You also need to be careful with using functions in a for loop. Within javascript a for-loop does not have a scope from itself. Only functions create new scopes.

gmap listener not working

I facing an issue that begin to make me crazy...
I'm sure you will be fresher than me to understand why the second marker listener is not working. :-(
In this code I get some item from an Ajax query in a JSON format.
Here is the javascript code:
// Get all items in JSON format
function getItems()
{
// Ajax call
$.getJSON("/index/test", function(data) {
createMenu(data);
fillMap(data);
});
}
function fillMap(data){
// For each
$.each(data, function(key, item) {
// Create markers
var latLon = new google.maps.LatLng(item.lat,item.lon);
marker = new google.maps.Marker({
position: latLon,
map: map,
title: 'Index: ' + item.id,
});
// Set marker on the map
marker.setMap(map);
// Listener 1
google.maps.event.addListener(marker, "click", function() {
map.setCenter(marker.getPosition());
});
// Listener 2 -
google.maps.event.addListener($('#resultList-'+item.id)[0], 'click', function() {
map.setCenter(marker.getPosition());
});
});
}
//Create the HTML menu
function createMenu(data){
var items = [];
//For each items
$.each(data, function(key, item) {
var imgHtml = '<img class="shadow" src="../images/item.png" height="48" width="48" alt="photo">';
// Create the HTML li tag
var html = '<li id="resultList-' + item.id + '" class="resultList">' + imgHtml + item.nom + ' ' + item.prenom + '<br/>' + item.adresse + '<br/>' + item.ville + ', ' + item.pays + '</li>';
items.push(html);
});
// Fill the div
$('<ul/>', {
'id': 'resultList',
'class': 'resultList',
html: items.join('')
}).appendTo('#divList');
//End...
}
Many thanks in advance!
Cedric.
If you want to add a Event listener in a non-gmaps object you should use google.maps.event.addDomListener
So you can try this:
google.maps.event.addDomListener(document.getElementById('resultList-' + item.id), 'click', function() {
map.setCenter(marker.getPosition());
});

Problems with javascript loop adding GMarker to GMap

I am having problems adding GMarkers when using a loop. The best way to explain the problem is to show the code, I guess :)
This works:
htmls[0] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[0].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[0].Username + "</strong> (" + result[1].Age + ")<br/>" + result[0].Country + "<br/>" + result[0].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[0] = new GLatLng(result[0].Latitude, result[0].Longitude);
if (result[0].Gender == "F") {
markers[0] = new GMarker(latlngs[0], { draggable: false, icon: fIcon });
} else {
markers[0] = new GMarker(latlngs[0], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[0], "click", function () {
markers[0].openInfoWindowHtml(htmls[0]);
});
map.addOverlay(markers[0]);
htmls[1] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[1].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[1].Username + "</strong> (" + result[1].Age + ")<br/>" + result[1].Country + "<br/>" + result[1].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[1] = new GLatLng(result[1].Latitude, result[1].Longitude);
if (result[1].Gender == "F") {
markers[1] = new GMarker(latlngs[1], { draggable: false, icon: fIcon });
} else {
markers[1] = new GMarker(latlngs[1], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[1], "click", function () {
markers[1].openInfoWindowHtml(htmls[1]);
});
map.addOverlay(markers[1]);
But when I put it in a loop, it doesn't work...
for (i = 0; i < result.length; i++) {
htmls[i] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[i].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[i].Username + "</strong> (" + result[i].Age + ")<br/>" + result[i].Country + "<br/>" + result[i].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[i] = new GLatLng(result[i].Latitude, result[i].Longitude);
if (result[i].Gender == "F") {
markers[i] = new GMarker(latlngs[i], { draggable: false, icon: fIcon });
} else {
markers[i] = new GMarker(latlngs[i], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[i], "click", function () {
markers[i].openInfoWindowHtml(htmls[i]);
});
map.addOverlay(markers[i]);
}
When using the loop, clicking on a marker breaks the script. It points to the line
markers[i].openInfoWindowHtml(htmls[i]);
And says that object is undefined. It also says that i = 10 at that point which is "impossible" as results.length is only 10
The problem is the classic function-in-a-loop. Here's one of the two typical ways to fix it:
function callback(i) {
return function () {
markers[i].openInfoWindowHtml(htmls[i]);
};
}
for (i = 0; i < result.length; i++) {
// snip...
GEvent.addListener(markers[i], "click", callback(i));
// snip...
}
JSLint can easily catch these common errors.
Edit
#Alex's answer shows roughly the other typical way that this problem is fixed, but with a few errors. This should work, though:
for (i = 0; i < result.length; i++) {
// snip...
GEvent.addListener(markers[i], "click", (function (i) {
return function () {
markers[i].openInfoWindowHtml(htmls[i]);
}
})(i));
// snip...
}
In this piece of code...
GEvent.addListener(markers[i], "click", function () {
markers[i].openInfoWindowHtml(htmls[i]);
});
...the function has closure to the i in the parent scope. So it is accessing the variable itself, not a copy of it.
At the end of the loop, when your function accesses the i variable, it will be equal to whatever condition stopped the loop, 10 in your example.
You can fix it with a self invoking anonymous function which passes the value to a new variable with a limited lifespan...
(function(j) {
GEvent.addListener(markers[j], "click", function () {
markers[j].openInfoWindowHtml(htmls[j]);
});
})(i);
Here is an example of similar code working.

Categories

Resources