Probably simple answer for someone that knows JavaScript, I don't know it very well. I'm getting JSON data back and applying markers to a map. However it's generating markers for those that have null which really messes things up. So what I need to do is create a conditional variable based on data being present. I have the following in the code:
let mapElement = document.getElementById('map-banner');
let pointMarkers = mapElement.getAttribute('data-location');
let marked = JSON.parse(pointMarkers);
let bounds = new google.maps.LatLngBounds();
console.log(pointMarkers);
marked.forEach(marked => {
if (marked.lat > 0 && marked.lat !== null) {
let lat = marked.lat;
}
let lng = marked.lng;
const marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: map,
icon: '/marker.png',
title: marked.name
});
bounds.extend(marker.position);
});
map.fitBounds(bounds);
};
Specifically I'm working with the variables lat and lng. I've also tried:
let lat = marked.lat;
if (lat > 0 && lat !== null) {
const lat = marked.lat;
}
In this case it presents all the data and it doesn't appear to be applying the condition.
You are conditionally declaring the variable, which for javascript is optional.
What you want is to skip that iteration in your loop with a guard clause:
marked.forEach(marked => {
if (marked.lat == null)
return;
const marker = new google.maps.Marker({
position: new google.maps.LatLng(marked.lat, marked.lng),
map: map,
icon: '/marker.png',
title: marked.name
});
bounds.extend(marker.position);
});
I think a filter is what you're looking for. Filtering can remove entries from arrays which you don't want to use.
Docs (by MDN) -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Also, const/let are used to achieve block scope in JavaScript.
consts are used for variables that do not change in value and are (preferably) immutable, see Docs -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
lets are used for values that do change in value, and have different values in different block scopes, see Docs -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
const mapElement = document.getElementById('map-banner');
const pointMarkers = mapElement.getAttribute('data-location');
// catch any error from JSON.parse
try {
const coords = JSON.parse(pointMarkers);
const bounds = new google.maps.LatLngBounds();
// filter out incorrect coords
const markers = coords.filter(coord => {
return (marked.lat > 0 && marked.lat !== null)
});
// create markers, extend bounds
markers.forEach(({ lat, lng }) => {
const marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map,
icon: '/marker.png',
title: marked.name
});
bounds.extend(marker.position);
});
// fit bounds
map.fitBounds(bounds);
} catch (error) {
// handle error to keep users happy
}
Maybe just show the marker if it exists?:
if(marked.lat && marked.lat > 0) {
const marker = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
map: map,
icon: '/marker.png',
title: marked.name
});
}
Related
I'm trying to assign the marker objects that I have added to my map into an array of markers to use in order to calculate the distances to each marker from a specific location. I begin with converting addresses from a text file that is fetched with a callback method that loops through each address and for each iteration sends a request to get the coordinates from the Geolocation API. In the geocode function, each request callback I add the corresponding LatLng to a new instance of a marker that is added to my map. Next, I try to push the marker onto my markers array.
When I try to console.log the values of the marker object through a button with an event listener, only empty objects are displayed.
I tried to declare the markers[] variable as a global variable. I'm still a beginner with Javascript but I think my issue may lie with the asynchronous action of the geocode request?
I get empty objects when I console log the markers array, although I get correct number of array elements (10 markers)
Here is my code:
let map;
let geocoder;
let markers = [];
window.onload = function() {
convertAddress();
document.getElementById('find_aed').addEventListener("click", function() {
let length = markers.length;
let aMarker;
for (let i = 0; i < length; i++) {
aMarker = markers[i]
console.log(aMarker.getPosition()); // ** This is where I print values **
}
});
}
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
zoom: 15,
center: { lat: 43.5327, lng: -80.2262 },
});
geocoder = new google.maps.Geocoder();
convertAddress();
const currLocation = new google.maps.LatLng(43.5327, -80.2262); object
console.log(currLocation.toString());
}
// convert address to latlng and add marker to map
function geocode(request) {
geocoder
.geocode(request)
.then((result) => {
const { results } = result;
// Add marker to map
let marker = new google.maps.Marker({
position: results[0].geometry.location,
map: map,
});
markers.push(marker); // ** This is where I push values to array **
return results; // return results
})
.catch((e) => {
alert("Geocode not successful for location:" + request + e);
})
}
// convert addresses into coordinates through google API request
function convertAddress() {
fetch('guelph_add.txt')
.then(response => response.text())
.then((text) => {
let addresses = text.split('\n');
let length = addresses.length;
let result;
console.log(length);
for (let i = 0; i < 10 ; i++) {
result = geocode ( {address: addresses[i]} );
}
});
}
window.initMap = initMap;
I am plotting thousands of data points on a map, and at any point I want to fetch the data points falling in a particular map bounding box. For example, attached below is the complete view of the available data points,
If I zoom in a bit, i will have fewer points and I want to fetch the data associated with those points.
How I am approaching this is fetching the bounding box of the map, and then checking the coordinates of each point if they belong in that bounding box. Sample code is below,
const data = [
{lat: 33.93911, lng: 67.709953},
{lat: -11.2027, lng: 17.8739},
{lat: -33.8688, lng: 151.2093},
{lat: -42.8821, lng: 147.3272},
{lat: 26.0275, lng: 50.55}
]
const boundingBox = window.map.getMapBounds();
const result = [];
for(let i = 0; i < data.length; i++) {
if(/* point lies within bounding box */) {
result.push(point);
}
}
How can I optimise it or is there a better solution? Thanks a lot.
You can try using bbox-polygon and boolean-intersects from turf:
Use onViewStateChange from deck instance within a debounce function so it won't be executed too often
Create a polygon from the deck viewState using #turf/bbox-polygon
Pass it to #turf/boolean-intersects with the whole features using filter JS method
import { WebMercatorViewport } from '#deck.gl/core';
import bboxPolygon from '#turf/bbox-polygon';
import intersects from '#turf/boolean-intersects';
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
fn.apply(this, args);
}, ms);
};
}
function getViewport(viewState) {
const bounds = new WebMercatorViewport(viewState).getBounds()
return bboxPolygon(bounds)
}
function getViewportFeatures({ viewState }) {
const viewport = getViewport(viewState)
const viewportFeatures = features.filter((f) => intersects(f, viewport))
console.log(viewportFeatures) // Your target
}
<DeckGL
...
onViewStateChange={debounce(getViewportFeatures, 500)}
/>
Advanced: if your data is really big, you can use Web Workers
I have a map where I need to show some locations based on the year the has selected. There are about 100k locations per year.
Everything works fine during the first load (year 2015). But if the user chooses another year (let's say year 2016), the browser will freeze.
Below is my code. I am callin markers.clearLayers() in order to remove the current points (from year 2015) and update the map with the new ones (year 2016).
var map = L.map('map', {
center: latlng,
zoom: 10,
layers: [tiles]
});
var markers = L.markerClusterGroup({
chunkedLoading: true
});
fetchLocations(yearSlider.value)
function fetchLocations(year) {
markers.clearLayers();
fetch('http://localhost:3003/locations/year/' + year)
.then(response => response.json())
.then(json => {
for (var i = 0; i < json.length; i++) {
const geo = json[i].geo;
const coords = geo.coordinates;
var marker = L.marker(L.latLng(coords[1], coords[0]), {
title: "abc"
});
marker.bindPopup("abc");
markers.addLayer(marker);
}
map.addLayer(markers);
});
}
First way is to reduce the visible markers for just what is on screen. This will increase a lot of performance.
var markers = new L.MarkerClusterGroup(....);
// Remove everything outside the current view (Performance)
markers._getExpandedVisibleBounds = function () {
return mcg._map.getBounds();
};
Second way is increasing maxClusterRadius will give you less clusters over the map. This also improve dragging performance.
Other freezing issue might be creating L.marker(...) of 100k locations.
Editted:
fetchLocations(yearSlider.value)
var map;
function initMap () {
// Reset whole map and redraw it.
if (map) {
map.off();
map.remove();
}
// Create a new leaflet map
map = L.map('map', {
center: latlng,
zoom: 10,
layers: [tiles]
});
return;
}
function fetchLocations(year) {
initMap();
fetch('http://localhost:3003/locations/year/' + year)
.then(response => response.json())
.then(json => {
// Create new marker cluster group
var markers = L.markerClusterGroup({
chunkedLoading: true
});
for (var i = 0; i < json.length; i++) {
const geo = json[i].geo;
const coords = geo.coordinates;
var marker = L.marker(L.latLng(coords[1], coords[0]), {
title: "abc"
});
marker.bindPopup("abc");
markers.addLayer(marker);
}
// Add individual group to the map
map.addLayer(markers);
});
}
The issue is that you are individually adding Markers to your Cluster Group while the latter is already on map:
function () {
// On next calls, `markers` is still on map
for (var i = 0; i < json.length; i++) {
markers.addLayer(marker); // synchronously processes when the group is on map
}
map.addLayer(markers); // on first call, `markers` is not yet on map
}
You can accumulate your markers in an array within your loop, then once it is full, you use markers.addLayers(arrayOfMarkers)
Or remove your markers group from map at the beginning of your function, so that it internally accumulates your Markers and batch processes them when it is added on map at the end.
Even though parndepu's solution also works, I managed to solve the problem by simply adding map.removeLayer(markers); after markers.clearLayers();.
i want to implement radius on my gmaps Autocomplete place search,so i can get search hint from that radius first.
my code
var options = {
types: [],
componentRestrictions: {
'country': 'IN'
},
location : { lat: 17.3850, lng: 78.4867 },
radius : 5000
};
let inputPick = document.getElementById('pick');
const autocompletePick = new google.maps.places.Autocomplete(inputPick, options);
google.maps.event.addListener(autocompletePick, 'place_changed', () => {
let place =autocompletePick.getPlace();
// console.log(place.name)
// document.getElementById('pick').value = place.name;
this.setState({pickAddress:place.name})
let lat = place.geometry.location.lat(),
lng = place.geometry.location.lng();
//putting place in pick field
let geocoder = new google.maps.Geocoder;
geocoder.geocode({'location': {lat:lat,lng:lng}}, function(results, status) {
if (status === 'OK') {
// console.log(results[0].formatted_address)
document.getElementById('pick').value = results[0].formatted_address;
}
})
but right now i'm getting all over country place.
i search on google but didn't get proper solution.can anyone help me,i''m stuck ):
If you check the Google Maps JavaScript API reference documentation, you will see that AutocompleteOptions object doesn't have location and radius fields
https://developers.google.com/maps/documentation/javascript/reference/3/#AutocompleteOptions
Instead it has bounds field to define an area where you would like to search and strictBounds field to restrict results to this area only.
You should rewrite your code with bounds and strictBounds autocomplete options. Something like
var options = {
bounds: cityBounds,
strictBounds: true,
types: ['geocode'],
componentRestrictions: { country: "IN" }
};
Have a look at bounds documentation for more details
https://developers.google.com/maps/documentation/javascript/reference/3/#LatLngBounds
https://developers.google.com/maps/documentation/javascript/reference/3/#LatLngBoundsLiteral
I hope this helps!
Able to successfully set the mapbox viewpoint dynamically by passing the geocoder a street address stored in my database.
But rather than just setting the map view to the address, I want to draw a marker at the address' location.
Template.vendorPage.rendered = function(){
//get address from database by ID
address = function(){
pathname =location.pathname.split("/");
thisId = pathname[2];
return Vendors.findOne({_id: thisId}).address
}
//set variable to the address function
thisAddress = address();
//draw the mapbox
L.mapbox.accessToken = '<My Token Here>';
var geocoder = L.mapbox.geocoder('mapbox.places-v1'),
map = L.mapbox.map('map', 'alexnetsch.j786e624');
geocoder.query(thisAddress, showMap);
function showMap(err, data) {
// The geocoder can return an area, like a city, or a
// point, like an address. Here we handle both cases,
// by fitting the map bounds to an area or zooming to a point.
if (data.lbounds) {
map.fitBounds(data.lbounds);
} else if (data.latlng) {
map.setView([data.latlng[0], data.latlng[1]], 16);
}
}
}
Played around with the documentation for hours and can't figure it out. I'd like to simply pass the marker function 'thisAddress'
Seems like rather than setting the viewport, I could set the map to be zoomedin and centered around my marker.
Here is the example from the documentation but without Geocoding the location.
L.mapbox.accessToken = 'pk.eyJ1IjoiYWxleG5ldHNjaCIsImEiOiJsX0V6Wl9NIn0.i14NX5hv3bkVIi075nOM2g';
var map = L.mapbox.map('map', 'examples.map-20v6611k')
.setView([38.91338, -77.03236], 16);
L.mapbox.featureLayer({
// this feature is in the GeoJSON format: see geojson.org
// for the full specification
type: 'Feature',
geometry: {
type: 'Point',
// coordinates here are in longitude, latitude order because
// x, y is the standard for GeoJSON and many formats
coordinates: [
-77.03221142292,
38.913371603574
]
},
properties: {
title: 'Peregrine Espresso',
description: '1718 14th St NW, Washington, DC',
// one can customize markers by adding simplestyle properties
// https://www.mapbox.com/foundations/an-open-platform/#simplestyle
'marker-size': 'large',
'marker-color': '#BE9A6B',
'marker-symbol': 'cafe'
}
}).addTo(map);
Figured it out finally.
Template.vendorPage.rendered = function(){
address = function(){
pathname =location.pathname.split("/");
thisId = pathname[2];
return Vendors.findOne({_id: thisId}).address
}
thisAddress = address();
//draw the mapbox
L.mapbox.accessToken = 'pk.eyJ1IjoiYWxleG5ldHNjaCIsImEiOiJsX0V6Wl9NIn0.i14NX5hv3bkVIi075nOM2g';
var geocoder = L.mapbox.geocoder('mapbox.places-v1'),
map = L.mapbox.map('map', 'alexnetsch.j786e624');
geocoder.query(thisAddress, showMap);
function showMap(err, data) {
// The geocoder can return an area, like a city, or a
// point, like an address. Here we handle both cases,
// by fitting the map bounds to an area or zooming to a point.
if (data.lbounds) {
map.fitBounds(data.lbounds);
} else if (data.latlng) {
map.setView([data.latlng[0], data.latlng[1]], 16);
}
}
var addMarker;
addMarker = function(geocoder, map, placeName) {
return geocoder.query(placeName, function(error, result) {
var marker;
marker = L.marker(result.latlng);
return marker.addTo(map);
});
};
addMarker(geocoder, map, thisAddress);