I am new to Mapbox. I am getting trouble while reading the features. I am only able to get the features which are listed or shown on current Viewport. I need to ask if there is any event in mapbox to check if all tiles/Features loaded inside my selected polygon so I can read all features between polygon. The code I am using for reading features is
map.on("idle", () => {
var features = map.queryRenderedFeatures(
[northEastPointPixel, southWestPointPixel],
{
layers: ["MyLayer"],
}
);
});
Related
I want to show the scale of my leaflet map using L.control.scale() however I want to position the scale the same way as the Google Maps API offers. I need the scale to be inline with map attribution. I have attached an image below.
Google Maps API (how i want it)
Leaflet (how it currently is)
I am using the following code to create the scale and add it to my map.
L.control.scale().addTo(map);
To simply put the scale in the same corner of the map as the attribution, you can just set the position option when you create the scale control:
L.control.scale({position:'bottomright', metric: false}).addTo(map);
To make the scale actually go inline with the attribution, you can create a custom control that includes both the scale widget and the attribution text. Leaflet is designed to be extensible, so you can extend the source code for L.Control.Scale to just add what you need:
L.Control.AttrScale = L.Control.Scale.extend({
onAdd: function (map) {
var className = 'leaflet-control-scale',
wrapper = L.DomUtil.create('div', 'leaflet-control-attr-scale'),
attribution = L.DomUtil.create('div', 'leaflet-control-attribution', wrapper),
container = L.DomUtil.create('div', className, wrapper),
options = this.options;
wrapper.style.display = "flex";
wrapper.style.alignItems = "center";
attribution.innerHTML = "My attribution string";
this._addScales(options, className + '-line', container);
map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
map.whenReady(this._update, this);
return wrapper;
},
});
map.addControl(new L.Control.AttrScale({position:'bottomright', metric: false}))
The example above shows a static attribution string before the scale. You can still use the normal options to position the control and set whether to show the metric and/or imperial scale. You will probably also want to create the map with the attributionControl: false option, to avoid the default attribution showing.
If you need to dynamically set the attribution message based on the map selection (like the normal attribution control does), look at the source code for L.Control.Attribution to see what extra functionality you need to incorporate into the custom control.
I am currently working on a program that pulls countries and coordinates from a database. I am pinning them on a map after gathering them. I am having no trouble when receiving both the coordinates and the country but when the coordinates aren't available I can't pin on the map just based on the country. I was hoping there was a leaflet method that would allow me to geolocate solely based on a country that would pin in the middle of the country.
Any help is appreciated, thanks!
There is no core Leaflet method for doing this. However, there are some (3rd-party) Leaflet geocoding plugins that might do the trick. Take at look here: http://leafletjs.com/plugins.html#geocoding. Specifically, there is a geonames plugin that looks especially promising: https://github.com/consbio/Leaflet.Geonames.
Leaflet or not, this should be fairly easy to accomplish using a simple RESTful API call. Here's a Plunker where this is done: https://plnkr.co/edit/B2OF0D1nlBuNW6r2rqVL
Below is the operative section:
$( document ).ready(
function() {
// Get the latlng from rest countries
getCountryLocation = function() {
$('#country').html( $('#country_input').val() );
$.ajax(
{
url: "https://restcountries.eu/rest/v2/name/" + $('#country_input').val() + "?fullText=true",
success: function( result ) {
var latlng = result[0].latlng;
$("#latlng").html( latlng[0] + ", " + latlng[1] );
}
}
);
}
// Do the inital setup
getCountryLocation();
$('#go').click(
getCountryLocation
);
}
);
Another developer created our original map but I'm tasked with making some changes. One of these is making sure the activated marker is brought to the front when clicked on (where it is partially overlapped by other markers).
The developers have used mapbox 2.2.2.
I have looked at leafletjs's docs, have followed some instructions on other posted solutions (e.g. solution one and solution two). Neither of these makes any difference.
Examining the marker in Chrome's console I can see the value of options.zIndexOffset is being set (10000 in my test case). I've even set _zIndex to an artificially high value and can see that reflected in the marker's data structure. But visually nothing is changing.
This is how the map is set up initially. All features are from a single geojson feed:
L.mapbox.accessToken = '<access token here>';
var map = L.mapbox.map('map', 'map.id', {
}).setView([37.8, -96], 3);
var jsonFeed, jsonFeedURL;
var featureLayer = L.mapbox.featureLayer()
.addTo(map)
.setFilter(function (f) {
return false;
});
$.getJSON(jsonFeedURL, function (json) {
jsonFeed = json;
jsonFeedOld = json;
// Load all the map features from our json file
featureLayer.setGeoJSON(jsonFeed);
}).done(function(e) {
// Once the json feed has loaded via AJAX, check to see if
// we should show a default view
mapControl.activateInitialItem();
});
Below is a snippet of how I had tried setting values to change the z-index. When a visual marker on the featureLayer is clicked, 'activateMarker' is called:
featureLayer.on('click', function (e) {
mapControl.activateMarker(e);
});
The GEOjson feed has urls for the icons to show, and the active marker icon is switched to an alternative version (which is also larger). When the active feature is a single Point I've tried to set values for the marker (lines commented out, some of the various things I've tried!)
activateMarker: function (e) {
var marker = e.layer;
var feature = e.layer.feature;
this.resetMarkers();
if (feature.properties.hasOwnProperty('icon')) {
feature.properties.icon['oldIcon'] = feature.properties.icon['iconUrl'];
feature.properties.icon['iconUrl'] = feature.properties.icon['iconActive'];
feature.properties.icon['oldIconSize'] = feature.properties.icon['iconSize'];
feature.properties.icon['iconSize'] = feature.properties.icon['iconSizeActive'];
}
if (feature.geometry.type == 'Point') {
marker.setZIndexOffset(10001);
marker.addTo(featureLayer);
}
//featureLayer.setGeoJSON(jsonFeed);
}
Any advice would be greatly appreciated! I'm at the point where I don't know what else to try (and that's saying something).
What probably happens is that you just flush your markers with the last call to .setGeoJSON():
If the layer already has features, they are replaced with the new features.
You correctly adjust the GeoJSON data related to your icon, so that when re-created, your featureLayer can use the new values to show a new icon (depending on how you configured featureLayer).
But anything you changed directly on the marker is lost, as the marker is removed and replaced by a new one, re-built from the GeoJSON data.
The "cleanest" way would probably be to avoid re-creating all features at every click.
Another way could be to also change something else in your GeoJSON data that tells featureLayer to build your new marker (through the pointToLayer option) with a different zIndexOffset option.
I'm building an online version of a boardgame using Leaflet as the viewport.
My source: https://github.com/edenLOL/gotta-chug-em-all/tree/master
What I'm trying to achieve, is a way to spiderfy/spread out markers that land on the same square as each other, including when they move past each other.
I'm using https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet to Spiderfy the markers, which appears to work when clicking the markers.
However, I need these markers to be spiderfied always, without requiring a click event. So I set keepSpiderfied: true for the spiderfier object (oms).
var options = { //**spiderfier
keepSpiderfied: true,
nearbyDistance: 120
};
oms = new OverlappingMarkerSpiderfier(map, options); //**spiderfier
But this doesn't work. I can't seem to load the markers in a spiderfied state, it always requires a click to spiderfy them.
For context, markers are generated dynamically during a prompt before the user sees the map.
('#playerIcons > table > tbody > tr > td').on('click', function(){
pokemonSelected = $(this).attr('class');
if ( !$(this).hasClass('pokemonSelected') ){
playerArray[pokeCounter].pokemon = ''+ pokemonSelected +'';
playerArray[pokeCounter].marker = L.marker([playerArray[pokeCounter].coords[0], playerArray[pokeCounter].coords[1]], {
icon: window[pokemonSelected]
}).addTo(map);
oms.addMarker(playerArray[pokeCounter].marker) //**spiderfier
pokeCounter += 1;
} else {
return false;
};
});
I've also tried using Leaflet/Leaflet.markercluster. This works to an extent, and spiderfies the Markers at defined zoom levels, however the markers don't move, and stay stacked on top of each other.
What am I missing here that could be causing these issues? I don't mind using either Spiderfier or MarkerCluster, as both should be able to provide the solution I'm looking for once either issue is fixed.
Spiderfier: Markers need to spiderfy automatically
MarkerCluster: Markers don't physically move when spiderfying out of cluster
Note: If you decide to open index.html, be aware the Pokemon theme song will autoplay in the background on page load (until the map is drawn), so check your volume :)
I solve the problem like this, using fire('click') аfrom leaflet.
scope.spiderMarker - the marker, i want to spidrefy. In your case i think its playerArray[pokeCounter].marker
try{
scope.spiderMarker.fire('click')}
catch (e) {
console.log(e);
};
I have implemented a geojson filter using toggle buttons with a LayerGroup, but would like to know if anyone has been successful with same behavior using on-map mouseclicks.
Example: map of world countries. click on Italy polygon results in only Italy being visible. Click outside Italy to show all countries again. Hope my question is clear.
It's just a matter of hooking on to the click event of the layer, clearing the group and add that single layer. Also hook on to the map click, remove the single layer and restore the rest. Here's a quick-n-dirty example:
// vars to store stuff
var geojson, source, selected;
// Load the collection
$.getJSON(url, function (collection) {
// Store collection for later use
source = collection;
// Create layer and add collection
geojson = L.geoJson(collection, {
// On each feature in collection
'onEachFeature': function (feature, layer) {
// Attach click handler
layer.on('click', function () {
// Set selected flag
selected = true;
// Clear the entire layer
geojson.clearLayers();
// Add the feature
geojson.addData(feature);
// Fit layer to map
map.fitBounds(layer.getBounds());
});
}
}).addTo(map);
});
// Attach to map click
map.on('click', function () {
// Check if something's selected
if (selected) {
// Clear the entire layer
geojson.clearLayers();
// Restore the collection
geojson.addData(source);
// Fit map to collection
map.fitBounds(geojson.getBounds());
}
});
Here's a working example on Plunker: http://plnkr.co/edit/o5Q0p3?p=preview