Trouble with accessing array of data in Javascript - javascript

I'm trying to write a little web app that will map some locations on a map using Google's Maps JavaScript API. Working with the API is going well, but I'm trying to manipulate their example code and I cannot get the array of data to work unless it's declared withing the Map initialization function. I've been digging for what I am not understanding about Javascript variable access, but I can't figure it out. Ideally I would like to put the array of data in a separate <script> tag so I can load it from another file, but I can't even get the data to work if placed right above the function within the same <script> tag.
I here is a simplified version of the code that I cannot get to work. It won't run because I removed my key from the call to the API, but if that's needed to find the problem I can give it out too.
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
// <script src="...">
// where I really want to load let locations = .... from a file
// </script>
<script>
// where i tried to move let locations = .... without success
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 2.7,
center: new google.maps.LatLng(18,0)
});
let locations = [
{
name: 'MJ Cave',
position: new google.maps.LatLng(47.517869, 19.036026)
}, {
name: 'Protec Tulum',
position: new google.maps.LatLng(20.216557, -87.460052)
}, {
name: 'Giraffe Manor',
position: new google.maps.LatLng(-1.375528, 36.744634)
}
];
function placeMarker( loc ) {
const marker = new google.maps.Marker({
position : loc.position,
map : map
});
}
// ITERATE ALL LOCATIONS. Pass every location to placeMarker
locations.forEach( placeMarker );
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=MYKEY&callback=initMap">
</script>
</body>
</html>

Problem solved.
Taking the new google.maps.latlng() calls out of the initMap() function was the problem because that datatype isn't necessarily defined at the time that code runs apparently. Changed the location position object to be defined by an array of two numbers and then made them into a google.maps.latlng in function. Works as desired now. For postarity here's my changed code:
<script>
let locations = [
{
name: 'MJ Cave',
position: [47.517869, 19.036026]
}, {
name: 'Protec Tulum',
position: [20.216557, -87.460052]
}, {
name: 'Giraffe Manor',
position: [-1.375528, 36.744634]
}
];
</script>
<script type="text/javascript">
function initMap() {
const infowindow = new google.maps.InfoWindow();
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 2.7,
center: new google.maps.LatLng(18,0)
});
function placeMarker( loc ) {
const marker = new google.maps.Marker({
position : { lat: loc.position[0], lng: loc.position[1]},
icon: icons[loc.type].icon,
map : map
});
// ITERATE ALL LOCATIONS. Pass every location to placeMarker
locations.forEach( placeMarker );
}
</script>

Related

Remove google.maps.marker.AdvancedMarkerView from map

I have a map, which populates markers based on search. I'm attempting to user the newer google maps feature AdvancedMarkerView so I can fill it with custom HTML - however, as my search updates, I want to flush the old markers and place new ones when it's called for...and I can't for the life of me figure out how to? https://developers.google.com/maps/documentation/javascript/reference/advanced-markers
The following places the custom markers. It works.
const content = document.createElement('div');
content.className = 'marker-title';
content.textContent = item.title;
const marker = new google.maps.marker.AdvancedMarkerView({
map,
position: item.position,
content
});
Normally for markers, as in the old markers, I've removed them with the following code, markers.forEach((marker) => marker.setMap(null)) however this doesn't seem to work for the advanced markers. Since the marker returned when creating the advanced marker points to the element, I also tried doing a marker.remove() thinking the HTML element would be targeted, but no cigar.
I haven't been able to find any concrete examples on the Google API docs, when it comes to advanced markers, and same for others asking the same question.
There is no setMap() or other method to call on the AdvancedMarkerView class to toggle its visibility or remove it from the map.
Although it is not super clear, the documentation says:
To remove a marker from the map, set the markerView.map property to null.
Working example below:
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 37.39094933041195, lng: -122.02503913145092 },
zoom: 14,
mapId: "4504f8b37365c3d0",
});
const draggableMarker = new google.maps.marker.AdvancedMarkerView({
map,
position: { lat: 37.39094933041195, lng: -122.02503913145092 },
draggable: true,
title: "This marker is draggable. Click to remove.",
});
draggableMarker.addListener("click", (event) => {
// Remove AdvancedMarkerView from Map
draggableMarker.map = null;
});
map.addListener("click", (event) => {
// Set AdvancedMarkerView position and add to Map
draggableMarker.position = event.latLng;
draggableMarker.map = map;
});
}
window.initMap = initMap;
#map {
height: 160px;
}
p {
font-family: Arial;
font-size: 0.75em;
margin: 2px 0;
}
<!DOCTYPE html>
<html>
<head>
<title>Draggable Advanced Marker</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- jsFiddle will insert css and js -->
</head>
<body>
<p>Click marker to remove it from map. Click on map to reposition / add marker.</p>
<div id="map"></div>
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=marker&v=beta"
defer
></script>
</body>
</html>

Google maps API use dragend to update one of multiple markers

I'm using google maps API with multiple equipment locations fed from a Mysql database. I can successfully show the markers on the map and make them draggable. If I drag any marker to a new location, it prompts to ask if you want to update the database and if yes, supposedly sends the updated lat/lng to the database. This isn't working correctly, and instead always only updates the last item in the markers array.
Note that I have done this successfully with a single marker, and am able to update it anywhere I drag the new marker. But for multiple markers, I just don't seem to be able to get the current dragged marker's id and new lat/lon data to send back to the ajax call to update the database for that particular marker. I'm thinking it's because I'm not accessing the current marker's data, but I'm struggling with how to get that.
Any ideas what I'm doing wrong? I know it's got to be something simple I'm overlooking.
Here's a Fiddle to demonstrate my issue: JSFiddle
<!-- php stuff would be up here to get the database and feed the google API. Instead here I've just created the const markers to show the markers. -->
<html>
<head>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
</head>
<body>
<div id="mapCanvas"></div>
</body>
</html>
<style>#mapCanvas {
width: 100%;
height: 850px;
}
</style>
<script>
function initMap() {
var map;
var bounds = new google.maps.LatLngBounds();
var mapOptions = {
mapTypeId: 'hybrid'
};
// Display a map on the web page
map = new google.maps.Map(document.getElementById("mapCanvas"), mapOptions);
map.setTilt(100);
// Multiple markers location, latitude, and longitude
const mapIcon = "https://maps.google.com/mapfiles/marker_grey.png";
const markers = [
['Pike-1', 35.42526738862006, -81.172076628969, mapIcon, 10819],
['Pike-2', 35.425311504805924, -81.17216095766817, mapIcon, 10820],
['Pike-3', 35.425171475622824, -81.17188553479076, mapIcon, 10821],
['Pike-4', 35.42530668524588, -81.17134696588283, mapIcon, 10822],
['Pike-5', 35.42559347700399, -81.17218140606506, mapIcon, 10823],
['Pike-5', 35.426094498781885, -81.17195101338058, mapIcon, 26455],
['Pike-6', 35.42532308059036, -81.17215505637606, mapIcon, 26456]
];
// Place each marker on the map
for (i = 0; i < markers.length; i++) {
var position = new google.maps.LatLng(markers[i][1], markers[i][2]);
var eid = markers[i][4];
bounds.extend(position);
marker = new google.maps.Marker({
position: position,
map: map,
draggable: true,
animation: google.maps.Animation.DROP,
icon: markers[i][3],
equipid: markers[i][4],
title: markers[i][0]
});
//Add listener if marker is moved, then ask to update and call function if yes.
google.maps.event.addListener(marker, 'dragend', function() {
var mytitle = marker.equipid;
var latLng = marker.latLng;
let updateDB = confirm('Update location to database?');
if (updateDB) {
update_marker(latLng, mytitle);
console.log(latLng, mytitle);
}
});
// Center the map to fit all markers on the screen
map.fitBounds(bounds);
function update_marker(Marker, mytitle)
//This is the function that sends the update to the ajax call. Currently not working.
//with multiple markers, I can't get the selected marker equipment ID and new lat/lng.
{
//Update saved marker from DB and map using jQuery Ajax
var mLatLang = marker.getPosition().toUrlValue(); //get marker position - this does not get the marker position, but the last row of the result set from mysql
var mytitle = marker.equipid; //get marker equipment id - this currently gets the last row[id] of the mysql result set.
var myData = {
update: 'true',
name: mytitle,
latlang: mLatLang
};
$.ajax({
type: "POST",
url: "equipment-ajaxupdate-gps.php",
data: myData,
success: function(data) {
alert(data);
console.log(mytitle, mLatLang);
},
error: function(xhr, ajaxOptions, thrownError) {
alert("Error! Update Failed!")
console.log(mytitle, mLatLang);
}
});
}
} //end of for( i = 0)
// Set zoom level
var boundsListener = google.maps.event.addListener((map), 'bounds_changed', function(event) {
this.setZoom(19);
google.maps.event.removeListener(boundsListener);
});
} //End of function(initmap)
// Load initialize function
google.maps.event.addDomListener(window, 'load', initMap);
</script>
I made a few changes to your code - this seems to work OK as it sends the correct details via the ajax call so updating the db should be trivial from that point forth.
With the script tag that loads the Google Maps api you specify the callback function so there is no need to then manually call initMap function as you are with google.maps.event.addDomListener(window, 'load', initMap);
The update_marker function can be simplified to use a single argument that is the reference to the marker itself. This greatly simplifies accessing properties of the marker itself as you can see below where self is used within the function ( could be any name - it is a reference to the marker )
Rather than the clunky for (i = 0; i < markers.length; i++) { loop to iterate through the array elements a simpler forEach loop means you no longer need to use the name[i][2] type syntax - though if the actual data is coming from your db you'll likely be using JSON anyway which would necessitate a different loop strategy.
<?php
if( $_SERVER['REQUEST_METHOD']=='POST' && isset(
$_POST['update'],
$_POST['name'],
$_POST['latlang']
)){
$_POST['date']=date( DATE_RSS );
$payload=json_encode( $_POST );
exit( $payload );
}
?>
<html>
<head>
<style>
#mapCanvas {
width: 100%;
height: 850px;
}
</style>
<script src='//code.jquery.com/jquery-latest.js'></script>
</head>
<body>
<div id="mapCanvas"></div>
<script>
function initMap() {
var map;
var bounds = new google.maps.LatLngBounds();
var mapOptions = {
mapTypeId:'hybrid'
};
map = new google.maps.Map( document.getElementById("mapCanvas"), mapOptions );
map.setTilt(100);
const mapIcon = "https://maps.google.com/mapfiles/marker_grey.png";
const markers = [
['Pike-1', 35.42526738862006, -81.172076628969, mapIcon, 10819],
['Pike-2', 35.425311504805924, -81.17216095766817, mapIcon, 10820],
['Pike-3', 35.425171475622824, -81.17188553479076, mapIcon, 10821],
['Pike-4', 35.42530668524588, -81.17134696588283, mapIcon, 10822],
['Pike-5', 35.42559347700399, -81.17218140606506, mapIcon, 10823],
['Pike-6', 35.426094498781885, -81.17195101338058, mapIcon, 26455],
['Pike-7', 35.42532308059036, -81.17215505637606, mapIcon, 26456]
];
// Place each marker on the map.
// A forEach loop is, IMO, cleaner than the for(i=0;i<n;i++) {} etc
markers.forEach(a=>{
let title=a[0];
let latlng=new google.maps.LatLng( a[1], a[2] );
let icon=a[3];
let eid=a[4];
let marker=new google.maps.Marker({
position: latlng,
map: map,
draggable: true,
animation: google.maps.Animation.DROP,
icon:icon,
/* custom properties */
equipid:eid,
title:title
});
bounds.extend( latlng );
google.maps.event.addListener( marker, 'dragend', function(e) {
if( confirm('Update location to database?') ) {
/*
Within the context of this function `this` refers
to the marker itself that invoked the event handler.
Passing `this` as the function parameter to `update_marker`
allows us to access all properties of the marker from
within that function.
As the marker has been assigned custom properties when it was
added to the map we can access those easily within `update_marker`
*/
update_marker( this );
console.log('Update - Position:%s, Title:%s', this.getPosition().toUrlValue(), this.title );
}
});
function update_marker( self ){
/*
self is the marker reference
*/
$.ajax({
type:"POST",
url:location.href, //"equipment-ajaxupdate-gps.php"
data: {
update:'true',
name:self.title,
latlang:self.getPosition().toUrlValue()
},
success: function( response ) {
console.log( 'Ajax response: %o',response );
bounds.extend( latlng );
map.fitBounds( bounds );
},
error: function( error ) {
alert( "Error! Update Failed!\n"+error )
}
});
};
});//end forEach loop
map.fitBounds( bounds );
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=APIKEY&callback=initMap"></script>
</body>
</html>

Google Maps API KMZ file displaying wrong data on click event

I have a KMZ file that i load into my google maps application via link using javascript. The file works perfectly in Google Earth. The problem is in my application when i click in one of the many elements (areas): the returned description data is always from only one of the elements, not displaying the actual clicked, correct, element. This is what i've tried:
Check if the click event in the map is correct by placing a marker in the clicked position, it is correct.
Convert the data into KML using Google Earth, place it into my google drive as public, and using a direct download link from google drive in my application. It displayed the data but the error continued.
Created the most basic/blank application using just the layer to make sure anything else in my other application is interfering. Also didn't work.
The file is in this website: https://www.voanaboa.pt/codigo-drone named as "Regulamento RPA_ver_5.0.kmz”
Here's the only file that creates a basic application using the kmz file, i've removed my API key for privacy.
<!DOCTYPE html>
<html>
<head>
<title>Simple Map</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
var kmlLayer = new google.maps.KmlLayer();
var src = 'https://www.voanaboa.pt/Files/downloads/Regulamento-RPA-ver-5.0.kmz';
var kmlLayer = new google.maps.KmlLayer(src, {
//suppressInfoWindows: true,
preserveViewport: false,
map: map
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&libraries=geometry&callback=initMap"
async defer></script>
Most (but not all) of your placemarks have the same ID "ID_00000"). If I change that to be unique, the polygon's descriptions become unique:
example with unique ids
Per the KML reference, that doesn't have to be unique (it is a "stanard XML ID", but I am guessing the rendering code is assuming it is.
code snippet with updated kmz file:
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
<div id="map"></div>
<script>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {
lat: -34.397,
lng: 150.644
},
zoom: 8
});
var kmlLayer = new google.maps.KmlLayer();
var src = 'http://www.geocodezip.com/geoxml3_test/kmz/Regulamento-RPA-ver-5.0a.kmz';
var kmlLayer = new google.maps.KmlLayer(src, {
//suppressInfoWindows: true,
preserveViewport: false,
map: map
});
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&callback=initMap" async defer></script>

Javascript GoogleMaps API Animation Onclick for Array of points

I've searched the web but can't seem to locate an answer for my issue. I believe I'm close but just can't get this to work as intended.
I'd like to plot an array of locations on a google map and start with the DROP animation, then when a user clicks a point, I'd like it to bounce.
My current code does 1/2 the job, however, when you click it's targeting the last value in my array. The BOUNCE animation works but it doesn't seem to be applied to all values in my array. Can you point at what I'm missing in the code?
Thanks for all the help!
<html>
<head>
<!-- styles put here, but you can include a CSS file and reference it instead! -->
<style type="text/css">
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
// Create a map variable
var map;
var markers = [];
// Function to initialize the map within the map div
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40.74135, lng: -73.99802},
zoom: 10
});
// Create a single latLng literal object.
var locations = [
{title: 'Beer', location: new google.maps.LatLng(47.666633, -122.371453)},
{title: 'Home', location: new google.maps.LatLng(47.613141, -122.320587)},
{title: 'Work', location: new google.maps.LatLng(47.624812, -122.315134)}
];
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < locations.length; i++) {
var position = locations[i].location;
var title = locations[i].title;
var marker = new google.maps.Marker({
map: map,
position: position,
title: title,
animation: google.maps.Animation.DROP,
id: i
});
bounds.extend(marker.position);
google.maps.event.addListener(marker, 'click', function(){
if (marker.getAnimation() !== null) {
marker.setAnimation(null);
} else {
marker.setAnimation(google.maps.Animation.BOUNCE);
}
});
// markers.push(marker);
}
map.fitBounds(bounds);
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=&v=3&callback=initMap">
</script>
</body>
</html>
One option would be to use this inside the click event listener function to reference the clicked marker.
google.maps.event.addListener(marker, 'click', function(){
if (this.getAnimation() !== null) {
this.setAnimation(null);
} else {
this.setAnimation(google.maps.Animation.BOUNCE);
}
});
(you can also use function closure)

Confused About Map Updates

I'm very new to using Google Maps and very new to intricate javascript. Bearing this in mind, I'm trying to create a web map, with a feed from USGS. This feed is updated every 5 minutes. I'd like to have my map refresh every 5 minutes using this same feed (which is a geojson file).
My end goal is to have this and at least one other feed displayed/updated on my map. Over the past four days, I've gone through dozens of posts, and am at the point of being overloaded and confused. Will someone please clear my fog?
The code I'm posting is 99% not my code, mostly I've added comments so I can figure out what's going on in the code.
<HTML>
<HEAD>
<TITLE>TEST OF MAP</TITLE>
<STYLE>
/* --------------------------------------------------- */
/* Set the map height explicitly to define the size of */
/* the DIV * element that contains the map. */
/* ----------------------------------------------------*/
#map {
height: 75%;
border: 5px solid green;
}
</STYLE>
</HEAD>
<SCRIPT type="text/javascript">
// --------------------------------------
// Set a refresh interval in milliseconds
// --------------------------------------
setInterval(page_refresh, 1*60000);
google.maps.event.addDomListener(window, 'load', initialize);
</SCRIPT>
<BODY>
<H1><CENTER>MAP Demo</CENTER></H1>
<DIV id="map"></DIV>
<SCRIPT>
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 2,
center: new google.maps.LatLng(35.4437,139.6380),
mapTypeId: 'terrain'
});
// ---------------------------------------------------------
// Create a <SCRIPT> tag and set the USGS URL as the source.
// ---------------------------------------------------------
var script = document.createElement('script');
// -----------------------------------------------------------------------
// Using a local copy of the GeoJSON stored on the USGS server
//
http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_hour.geojsonp
// -----------------------------------------------------------------------
script.src =
'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_hour.geojson
p';
document.getElementsByTagName('head')[0].appendChild(script);
}
// ------------------------------------------------------------------
// Loop through the results array and place a marker for each set of
// coordinates.
// ------------------------------------------------------------------
window.eqfeed_callback = function(results) {
for (var i = 0; i < results.features.length; i++) {
var coords = results.features[i].geometry.coordinates;
var latLng = new google.maps.LatLng(coords[1],coords[0]);
var marker = new google.maps.Marker({
position: latLng,
map: map
});
}
}
</SCRIPT>
<SCRIPT async defer src="https://maps.googleapis.com/maps/api/js?
key=MY_MAP_KEY&callback=initMap">
</SCRIPT>
</BODY>
</HTML>
Though not very elegant, I used the following for the refresh. Since I'm only building a "proof of concept", refreshing the entire page is not a problem.
function timedRefresh(timeoutPeriod) {
setTimeout("location.reload(true);",timeoutPeriod);
}
window.onload = timedRefresh(60*5000);

Categories

Resources