Geojson AJAX calls interfere with onEachFeature functions - javascript

The purpose of the map is when a user selects a HTML input it pulls a geojson file and places the zip code information on the map for that state. Then a second geojson file will pull the state bounds and also put it on the map and zoom to that state. Currently I am trying to accomplish this with two Geojson AJAX calls. One is called geojson and the other is statelay (see below). When I just have the map run with only the geojson object it runs fine and the interaction with the zip codes runs smoothly. However, when I add statelay the interactivity no longer works with the first geosjon call. Here is what the interactivity looks like when it works: https://www.freepropertycalc.com/maps.
Here are the specific questions:
-Why does adding the statelay object below interfere with the interactivity of the other geojson call?
-How can I simply get the bounds from statelay and make the map zoom to those state bounds?
Code:
<select id="select" >
<option value="">State </option>
<option value="{{url_for('static', filename= 'AK.js' )}}">AK</option>
<option value="{{url_for('static', filename= 'AL.js' )}}">AL</option>
<!-- a whole bunch more states -->
</select>
</div>
<div id="map2" class = "content-section middle toppad" ></div>
<script type="text/javascript">
var state = '/static/state.js';
var select = document.getElementById('select') ;
select.addEventListener('change', function(){
var ourRequest = new XMLHttpRequest();
var strUser = select.options[select.selectedIndex].text;
ourRequest.open('GET',select.value)
ourRequest.onload = function(){
document.getElementById('map2').innerHTML = "<div id='llmap'></div>";
var TILE_URL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
var MB_ATTR = 'Map data © OpenStreetMap contributors';
map = L.map('llmap', {});
L.tileLayer(TILE_URL, {attribution: MB_ATTR}).addTo(map);
map.setView(new L.LatLng(38.500000, -98.000000), 4);
function onLocationError(e) {
alert(e.message);
}
// control that shows state info on hover
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = '<h6>Market Info by ZIP Code</h6>' + (props ?
'<b>' + 'ZIP: ' + props.ZIP + '</b><br />' +
'Median Home Property Value, May 2020: ' + props.valmay2020 + '<br />' +
'Median Home Sale Price, March 2020: ' + props.salemarch2020 + '<br />' +
'Median Rent, April 2020: ' + props.rentapr2020 + ', City Level Only' + '<br / >' +
'1yr. House Value Change: ' + props.chg1yrpropval +'%' + '<br />'
: 'Hover over a ZIP Code');
};
info.addTo(map);
// get color depending on population density value
function getColor(d) {
return d > 400000 ? '#800026' :
d > 300000 ? '#BD0026' :
d > 200000 ? '#E31A1C' :
d > 150000 ? '#FC4E2A' :
d > 100000 ? '#FD8D3C' :
d > 50000 ? '#FEB24C' :
d > 25000 ? '#FED976' :
'#FFEDA0';
}
function style(feature) {
return {
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7,
fillColor: getColor(feature.properties.valmay2020)
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#666',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
var geojson;
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function onEachFeature(feature, layer) {
layer.on({
click: highlightFeature,
mouseout: resetHighlight,
dbblick: zoomToFeature
});
}
var geojson = new L.GeoJSON.AJAX(select.value, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
var statelay = new L.GeoJSON.AJAX(state, {
filter: statefilter
}).addTo(map);
function statefilter(feature) {
if (feature.properties.STUSPS == strUser) return true
}
map.attributionControl.addAttribution('Housing and Rental data © Zillow');
var legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [0, 25000, 50000, 100000, 150000, 200000, 300000, 400000],
labels = [],
from, to;
for (var i = 0; i < grades.length; i++) {
from = grades[i];
to = grades[i + 1];
froml = grades[i]/1000 + 'k';
tol = grades[i +1]/1000 + 'k';
labels.push(
'<i style="background:' + getColor(from + 1) + '"></i> ' +
froml + (to ? '–' + tol : '+'));
}
div.innerHTML = labels.join('<br>');
return div;
};
legend.addTo(map);
};
ourRequest.send();
});

This is a easy fix.
Your interactivity is gone, because you add the state geojson over the interactivity geojson. Two solutions:
call statelay.bringToBack() when you want to display the area / border
don't add it to the map, if you don't want to show it. Remove .addTo(map) from var statelay = new L.GeoJSON.AJAX(...).addTo(map)
Zoom to the bounds: Wait until the geojson is loaded and the call fitBounds
statelay.on('data:loaded', function() {
map.fitBounds(statelay.getBounds())
})
But you will have a problem with zooming to the bounds for example with AK, because the shape is not correct (But that can't fixed by me):
And this is very lazy because this is a very big file ... so I recommand you a other way to zoom to the area:
zoom to the bounds of the geojson layer, then it is zoomed directly after the interactiv shapes are loaded.
geojson.on('data:loaded', function() {
map.fitBounds(geojson.getBounds())
})

Related

Leaflet : marker icons and markerCluster displaying together on the map with fetch

I am using Leaflet on my project. I work with PHP / Symfony, I retrieve the data from the Controller via fetch to display the markers on the map.
All markers are displayed on the map, but when using the markerCluster, markers that have close coordinates on the map (eg: same city) do not group together in clusters. The markers farther apart from each other group well together.
Have you an idea why ? Thanks
I am adding screens to you so that you can better understand :)
map.js
let iconPicture = L.icon({
iconUrl: "/assets/images/cycling.png",
iconSize: [20, 20],
popupAnchor: [0, -10],
});
function initMap() {
var map = L.map("mapId").setView([48.833, 2.333], 12);
var osmLayer = L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors",
minZoom: 3,
maxZoom: 19,
});
var markersGroup = L.markerClusterGroup({
disableClusteringAtZoom: 13,
spiderfyOnMaxZoom: false,
removeOutsideVisibleBounds: true,
});
fetch("/api/map")
.then((response) => {
return response.json();
})
.then((result) => {
result.forEach((element) => {
var cluster = L.marker([element.latitude, element.longitude], {
icon: iconPicture,
})
// Nous ajoutons la popup. A noter que son contenu peut être du HTMl
.bindPopup(
function (layer) {
return (
"<span>" +
element.name +
"</span>" +
"<br>" +
"<div class='img-hover-zoom'>" +
"<a href=" +
"/collection50_60/" +
element.id +
">" +
"<img class='picturePopup' src=/assets/images/uploads/" +
element.pictureFront +
">" +
"</a>" +
"</div>" +
"<br>" +
element.city +
"<br>" +
"<a href=" +
"/collection50_60" +
">" +
element.years +
"</a>"
);
},
{ className: "pop-up-leaflet", direction: "top" } //then add your options
)
.addTo(map);
markersGroup.addLayer(cluster);
});
map.addLayer(markersGroup);
})
.catch(() => console.error("error"));
}
window.onload = function () {
initMap();
};
That is just because you add your Markers (variable cluster) to both the map and the MarkerClusterGroup.
Simply add them only to MCG and let the latter handle everything.
var cluster = L.marker(latLng, options)
.bindPopup()
//.addTo(map); <= do not add your Marker directly to the map
markersGroup.addLayer(cluster); // <= let the MCG handle everything

How to move the leaflet map with mouse?

I try to move around the leaflet map with the mouse, but nothing comes out. In the createMap function, I create the required markers from the places array
The code:
function createMap (token, style, mapContainerSelector, places, attribution) {
var mapContainer = $(mapContainerSelector);
var arrayOfMarkers = [];
var map_id = "osm-map" + Math.random().toString(16).slice(2);
var map_div = document.createElement("div");
map_div.setAttribute("id", map_id);
mapContainer.append(map_div);
$.each(places, function(index, element) {
if (element[3] < -90) {
element[3] += 360;
}
arrayOfMarkers.push([
element[2],
element[3]
]);
});
var bounds = new L.LatLngBounds(arrayOfMarkers);
var map = L.map(map_id).fitBounds(bounds);
var gl = L.mapboxGL({
attribution: attribution,
accessToken: token,
style: style,
}).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: attribution,
}).addTo(map);
map.dragging.enable()
$.each(places, function(index, element) {
if (element[3] < -90) {
element[3] += 360;
}
L.marker([element[2], element[3]]).addTo(map)
.bindTooltip('День №' + element[1] + ', <b>' + element[0] + '</b><br>Прибытие: ' + element[4]);
});
}
Tell me how to do this? Thank you in advance!

How can I get the distance and time from L.Routing.control?

I am drawing a route on a leaflet map, it works good and in the control it shows the distance and estimated time of arrival. Is there a way to extract both of them and save them?
The code for the L.Routing.control
function getroute() {
myroutewithout = L.Routing.control({
waypoints: [
L.latLng(window.my_lat, window.my_lng),
L.latLng(window.job_p_lat, window.job_p_lng)
],show: true, units: 'imperial',
router: L.Routing.mapbox('API-KEY-HERE'),
createMarker: function(i, wp, nWps) {
if (i === 0 || i === nWps + 1) {
// here change the starting and ending icons
return mymarker = L.marker(wp.latLng, {
icon: operatoricon
});
} else {
return job_start = L.marker(wp.latLng, {
icon: jobicon
});
}
}
}).addTo(map);
You can achieve that using the code from this issue
var routeControl = L.Routing.control({...});
...
routeControl.on('routesfound', function(e) {
var routes = e.routes;
var summary = routes[0].summary;
// alert distance and time in km and minutes
alert('Total distance is ' + summary.totalDistance / 1000 + ' km and total time is ' + Math.round(summary.totalTime % 3600 / 60) + ' minutes');
});
Demo

How to change google maps marker icon dynamically

I am using ajax and php and I grabbing all of the points out of my database and plotting them on the map. Which works fine. However I want to change the icon of the marker depending on if status in the database is 100 or 200 or 300 for each record. I can't seem to get anything to work. Here is my code:
if (localStorage.getItem('type2') !== null) {
$(function ()
{
var radius2 = localStorage.getItem("radius2");
var lat2 = localStorage.getItem("lat2");
var long2 = localStorage.getItem("long2");
var type2 = localStorage.getItem("type2");
var city2 = localStorage.getItem("city2");
var rep2 = localStorage.getItem("rep2");
var size2 = localStorage.getItem("size2");
var status2 = localStorage.getItem("status2");
$.ajax({
url: 'http://example.com/Test/www/22233333.php',
data: "city2=" + city2 + "&rep2=" + rep2 + "&status2=" + status2 + "&size2=" + size2 + "&type2=" + type2 + "&long2=" + long2 + "&lat2=" + lat2 + "&radius2=" + radius2,
type: 'post',
dataType: 'json',
success: function (data) {
$.each(data, function (key, val) {
var lng = val['lng'];
var lat = val['lat'];
var id = val['id'];
var name = val['name'];
var address = val['address'];
var category = val['category'];
var city = val['city'];
var state = val['state'];
var rep = val['rep'];
var status = val['status'];
var size = val['size'];
$('div#google-map').gmap('addMarker', {
'position': new google.maps.LatLng(lat, lng),
'bounds': true,
'icon': 'images/hospital.png'
}).click(function () {
$('div#google-map').gmap('openInfoWindow', {
'backgroundColor': "rgb(32,32,32)",
'content': "<table><tr><td>Name:</td><td>" + name + "</td></tr><tr><td>Address:</td><td>" + address + ", " + city + " " + state + "</td></tr><tr><td>Category:</td><td>" + category + "</td></tr><tr><td>Rep:</td><td>" + rep + "</td></tr><tr><td>Status:</td><td>" + status + "</td></tr><tr><td>Size:</td><td>" + size + "</td></tr></table>"
}, this);
});
})
}
});
})
}
Looks like you are using jquery-ui-map?
I haven't used this abstraction
You can call the setIcon function - on a marker you can set it's icon this way for the main API
https://developers.google.com/maps/documentation/javascript/reference#Marker
So your addMarker method will return a marker instance by the look of it so once you have that run setIcon
Do something like this in your success function with in $.each.
Status is the database field
var size = val['size'];
var status = val['status'];
var icon = '';
if (status == 100){
icon = 'images/icon1.png'; //your icon1
}else if (status == 100){
icon = 'images/icon1.png'; //your icon2
}
...
$('div#google-map').gmap('addMarker', {
'position': new google.maps.LatLng(lat, lng),
'bounds': true,
'icon': icon //your dynamic icon
})
Hope this helps

Javascript insists it's not defined, it totally is

I'm not used to writing JS, honestly, and to make matters worse I'm working with the Google Maps API which, while well-documented, is a bear. So, I've written a function that allows a users to zoom onto the map from a link. But the interpreter insists my function isn't defined when I call it. The function is "zoomTo" and it appears in the following monster script.
function load() {
if (GBrowserIsCompatible()) {
var gmarkers = [];
var htmls = [];
var i = 0;
// Read the data
//++++++++++++++++
GDownloadUrl("/assets/data/nolag.xml", function(data) {
var xml = GXml.parse(data);
var markers =
xml.documentElement.getElementsByTagName("marker");
// Draw icons
for (var i = 0; i < markers.length; i++) {
var locoName = markers[i].getAttribute("locoName");
var speed = markers[i].getAttribute("speed");
var ip = markers[i].getAttribute("ip");
var date = markers[i].getAttribute("captureTime");
var lat = markers[i].getAttribute("lat");
var lng = markers[i].getAttribute("lng");
var location = markers[i].getAttribute("location");
var heading = markers[i].getAttribute("heading");
var type = markers[i].getAttribute("type");
var point = new GLatLng(parseFloat(markers[i].getAttribute("lat")),
parseFloat(markers[i].getAttribute("lng")));
var marker = createMarker(point, locoName, speed, ip, date, lat, lng, location, heading, type);
map.addOverlay(marker);
}
});
// create the map
var map = new GMap2(document.getElementById("map"));
map.addControl(new GLargeMapControl3D());
map.addControl(new GMapTypeControl());
map.setCenter(new GLatLng(40.5500, -72.1700), 7);
map.enableScrollWheelZoom();
NEC = new GPolyline(NECRoute, "#0d004c", 3, 0.7);
map.addOverlay(NEC);
// A function to create the marker and set up the event window
//+++++++++++++++++
function createMarker(point, locoName, speed, ip, date, lat, lng, location, heading, type) {
var marker = new GMarker(point, customIcons[type]);
marker.tooltip = '<div class="tooltip"><h1>' + locoName + '</h1><h2>' + speed + '<br/>' + location + '</h2></div>';
marker.contextmenu = '<div class="contextmenu">Hello world</div>';
var satellite = "<img src=\"./images/icons/satellite.gif\">";
var gps = "<img src=\"./images/icons/gps.gif\">";
var cmu = "<img src=\"./images/icons/cmu.gif\">";
var ftp = "<img src=\"./images/icons/ftp.gif\">";
var me1k = "<img src=\"./images/icons/me1k.gif\">";
var html = "<div class=\"bubble\">";
html += "<h3>" + locoName + "<span class=\"small-data\"> Route 2150</span></h3>";;
html += "<div class=\"toolbar\">" + gps + satellite + cmu + ftp + me1k + "</div>";
html += "<h4>Heading " + heading + " # " + speed + " MPH</h4>"
html += "<h4>Lat: " + lat + "</h4><h4> Lng: " + lng + "</h4>";
html += "<h4>IP: " + ip + "</h4>";
html += "<h4><div class=\"sm-button\"><a style='color: #FFFFFF; decoration:none;' href='map_detail.php'>Details</a></div><div class=\"sm-button\">Zoom To</div></h4>";
html += "</div>";
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml(html);
});
gmarkers[i] = marker;
htmls[i] = html;
i++;
// ====== The new marker "mouseover" and "mouseout" listeners ======
GEvent.addListener(marker,"mouseover", function() {
showTooltip(marker);
});
GEvent.addListener(marker,"mouseout", function() {
tooltip.style.visibility="hidden"
});
return marker;
}
// ====== This function displays the tooltip ======
//+++++++++++++++++++++++++++++++++++++++++++++++++++
function showTooltip(marker) {
tooltip.innerHTML = marker.tooltip;
var point=map.getCurrentMapType().getProjection().fromLatLngToPixel(map.getBounds().getSouthWest(),map.getZoom());
var offset=map.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(),map.getZoom());
var anchor=marker.getIcon().iconAnchor;
var width=marker.getIcon().iconSize.width;
var pos = new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(offset.x - point.x - anchor.x + width,- offset.y + point.y +anchor.y));
pos.apply(tooltip);
tooltip.style.visibility="visible";
}
// ===== This function is invoked when the mouse goes over an entry in the side_bar =====
function mymouseover(i) {
showTooltip(gmarkers[i])
}
// ===== This function is invoked when the mouse leaves an entry in the side_bar =====
function mymouseout() {
tooltip.style.visibility="hidden";
}
// This function picks up the side_bar click and opens the corresponding info window
function myclick(i) {
gmarkers[i].openInfoWindowHtml(htmls[i]);
}
// ====== set up marker mouseover tooltip div ======
var tooltip = document.createElement("div");
document.getElementById("map").appendChild(tooltip);
tooltip.style.visibility="hidden";
// === create the context menu div ===
var contextmenu = document.createElement("div");
contextmenu.style.visibility="hidden";
// === functions that perform the context menu options ===
function zoomTo( lat, lng, level) {
// perform the requested operation
map.setCenter( new GLatLng(lat,lng), level);
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}
contextmenu.innerHTML = '<div class="context"><ul>'
+ '<li class="sectionheading">Zoom Out</li>'
+ '<li class="sectionheading">Boston Area</li>'
+ '<li>South Station, Boston</li>'
+ '<li>South Hampton</li>'
+ '<li>New Haven</li>'
+ '<li class="sectionheading">New York Area</li>'
+ '<li>Sunny Side</li>'
+ '<li>NY, Penn Station</li>'
+ '<li>30th Street, Philadelphia</li>'
+ '<li>Wilmington Shops</li>'
+ '<li>Baltimore, Penn Station</li>'
+ '<li class="sectionheading">DC Area</li>'
+ '<li>DC, Ivy City</li>'
+ '<li>DC, Union Station</li>'
+ '</ul</div>';
map.getContainer().appendChild(contextmenu);
// === listen for singlerightclick ===
GEvent.addListener(map,"singlerightclick",function(pixel,tile) {
clickedPixel = pixel;
var x=pixel.x;
var y=pixel.y;
if (x > map.getSize().width - 120) { x = map.getSize().width - 120 }
if (y > map.getSize().height - 100) { y = map.getSize().height - 100 }
var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(x,y));
pos.apply(contextmenu);
contextmenu.style.visibility = "visible";
});
// === If the user clicks on the map, close the context menu ===
GEvent.addListener(map, "click", function() {
contextmenu.style.visibility="hidden";
});
} else {
alert("Sorry, the Google Maps API is not compatible with this browser");
}
}
It appears that zoomTo is an annonymous function inside of the top-level function load() - therefore, it's not available to top-level calls.
Instead of
// === functions that perform the context menu options ===
function zoomTo( lat, lng, level) {
// perform the requested operation
map.setCenter( new GLatLng(lat,lng), level);
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}
put the function in the top level, and pass in the map and contextmenu variables:
// === functions that perform the context menu options ===
function zoomTo( lat, lng, level) {
// perform the requested operation
map.setCenter( new GLatLng(lat,lng), level);
// hide the context menu now that it has been used
contextmenu.style.visibility="hidden";
}
As it dont looks well in comment. posting the comment above here again
<html>
<head>
<script type="text/javascript">
function zoomTo(x,y,z) {
....
}
</script>
.....
</head>
......
</html>

Categories

Resources