MapBox Symbol doesn't act like markers properly (clustering, click..) - mapbox

I am trying to make moving cars on MapBox using new promising GL Symbol layer/source. It looks very nice both on android and ios, but I faced with two impossibilities.
Symbols are always clustering. setIconAllowOverlap() and setIconIgnorePlacement() don't help: on some zoom it WILL be clustered. On both platforms.
How can I disable symbols clustering completely?
UPDATE: the code and even fast solution! (possibly bug? see comment at withTextField)
in onStyleLoaded():
...
carManager = new SymbolManager(mapView, mapboxMap, style);
carManager.setIconAllowOverlap(true);//doesn't help
carManager.setIconIgnorePlacement(true);//doesn't help
...
in drawCarFunction():
...
SymbolOptions carOptions = new SymbolOptions()
.withLatLng(latLng)
.withIconImage(carPlate)
//.withTextField(carPlate) //!!!! here it will cluster if text exists, and will NOT - without any text
;
Symbol car= carManager.create(carOptions);
carSymbols.add(car);
...
Next question:
On Android we have symbolManager.addClickListener() , but how could I catch a click on iOS? I know I can catch the tap, calculate nearest marker etc but
How to get symbol click simpler in swift?
In Moving cars task I should enumerate existing cars, move running, add newest. Where should I store cars IDs to get it later on next move? Where are no even symbol.setTag() option... Storing IDs in snippet (as on GMaps) is not that choice I expected from MapBox. Sure, I can make an array of pairs "car ID = symbol ID", but
How to store my own UID in the symbol?
UPDATE: the code. Note the comment near getTag()
void moveExistingCarOrAddNew(int carId, LatLng newLocation){
for (int i = 0; i < carManager.getAnnotations().size(); i++) {
if (carManager.getAnnotations().get(i).getTag()==carId){ //but no getTag() here, I should fit data into text fields
car.setLatLng(newLocation); //move!
} else {
...//create new marker as shown above }
}
}
}

Related

leafeltjs (mapbox) z-index ordering not working

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.

Leaflet Spiderfier/MarkerCluster - Automatically spiderfy dynamic markers

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);
};

Why doesn't marker.dragging.disable() work?

The following code receives an error on the lines for enabling and disabling the marker dragging ("Unable to get property 'disable' of undefined or null reference"). The markers show up on the map just fine and are draggable as the creation line indicates. Placing an alert in place of the enable line produces a proper object so I believe the marker is defined. Is there something I need to do to enable the IHandler interface? Or am I missing something else?
var marker = L.marker(L.latLng(lat,lon), {icon:myIcon, draggable:'true'})
.bindLabel(name, {noHide: true,direction: 'right'});
marker._myId = name;
if (mode === 0) {
marker.dragging.enable();
} else {
marker.dragging.disable();
}
I had a similar problem today (perhaps the same one) it was due to a bug in leaflet (see leaflet issue #2578) where changing the icon of a marker invalidates any drag handling set on that marker. This makes any calls to marker.dragging.disable() fail.
The fix hasn't made it into leaflets master at time of writing. A workaround is to change the icon after updating the draggable status if possible.
marker.dragging.disable();
marker.setIcon(marker_icon);
Use the following code to make an object draggable. Set elementToDrag to the object you wish to make draggable, which is in your case: "marker"
var draggable = new L.Draggable(elementToDrag);
draggable.enable();
To disable dragging, use the following code:
draggable.disable()
A class for making DOM elements draggable (including touch support).
Used internally for map and marker dragging. Only works for elements
that were positioned with DomUtil#setPosition
leaflet: Draggable
If you wish to only disable the drag option of a marker, then you can use the following code (where "marker" is the name of your marker object):
marker.dragging.disable();
marker.dragging.enable();
I haven't found an answer but my workaround was this:
var temp;
if (mode === 0) {
temp = true;
} else {
temp = false;
}
var marker = L.marker(L.latLng(lat,lon), {icon:myIcon, draggable:temp})
.bindLabel(name, {noHide: true,direction: 'right'});
marker._myId = name;
Fortunately I change my icon when it is draggable.

How to prevent overlapping pushpins at the same geolocation with Bing Maps?

If you have 2 pushpins on 'London' at the same geolocation, is there anything in the API to move them apart so they are both visible?
I can only find documentation on their old map points API which had PreventIconCollisions, this is what I want but can't see any reference to this in the new API.
I am using the JavaScript API.
So if I understand correctly, you have similar information on the same location, it this correct?
In order to display both information, you will have two options:
Merge information in the textbox using an appropriate way to present the information inside this ui element (using your own tabbed infobox for example)
Decluster the point manually when you're at a certain level of zoom
There is no default property to set this and it would really messy to do this on many pushpins, but in the main idea, you would have to: detect viewchangeend event, if you're at a certain level of zoom (or higher zoom level) then you're declustering them (I call it decluter nearby pushpins).
// Bind pushpin mouseover.
Microsoft.Maps.Events.addHandler(pin, 'mouseover', function (e) {
var currentPin = e.target;
currentPin.setOptions({ visible: false });
var currentLocation = currentPin.getLocation().clone();
var currentPoint = bmGlobals.geo.map.tryLocationToPixel(currentLocation);
if (currentPin.associatedCluster.length == 2) {
// Display the first pushpin
var pinA = createPin(currentPin.associatedCluster[0]);
var locA = bmGlobals.geo.map.tryPixelToLocation(new Microsoft.Maps.Point(currentPoint.x - pinA.getWidth(), currentPoint.y));
pinA.setLocation(locA);
bmGlobals.geo.layerClusteredPin.push(pinA);
// Display the second pushpin
var pinB = createPin(currentPin.associatedCluster[1]);
var locB = bmGlobals.geo.map.tryPixelToLocation(new Microsoft.Maps.Point(currentPoint.x + pinB.getWidth(), currentPoint.y));
pinB.setLocation(locB);
bmGlobals.geo.layerClusteredPin.push(pinB);
}
});
I will try to write a bing maps module about this, but in the fact, you'll have to get your clustered pushpins (or your own pushpin that has two associated data object) and then you will have to set their position based on the rendering on the client side.
I know this question is really old, but if someone is looking for something similar (clustering the pins) here is a good start: http://rtsinani.github.io/PinClusterer/

Coverage map with Drupal, GMap and Location

I'm using the GMaps, Location and User locations module on my Drupal site. I would like to have a map which can display the "coverage" of my users. The coverage radius is stored in a custom content type with reference to the user.
How is it possible to display users with only their coverage circle on a map? I saw a display type like this when playing with the GMap macros, so I think it would be possible to use GMaps that way.
I suppose most likely this isn't supported out-of-the-box with the GMap module. Anyway, I would be really glad if anybody could point me into the right direction...
I've managed to do this after all with the help of the Hungarian community. (I would be happy to link the original thread, but it seems it got deleted.)
Basically the "trick" was to make a little custom module with a hook:
function MODULENAME_preprocess_gmap_view_gmap(&$vars) {
if ( $vars['view']->name == "my_gmap_view" ) {
$map_object = $vars['map_object'];
$map_object['id'] = 'my_view_id';
foreach ($vars['view']->result as $key => $row) {
$shapes[$key]['type'] = 'circle';
// we have a profile field for the radius, but it could be anything...
$shapes[$key]['radius'] = $row->profile_values_profile_radius_value;
// center the circles on the coord
$shapes[$key]['center'][0] = $row->location_latitude;
$shapes[$key]['center'][1]= $row->location_longitude;
}
// we don't need any markers, just the circles
$map_object['markers'] = NULL;
$map_object['shapes'] = $shapes;
$vars['map'] = theme('gmap', array('#settings' => $map_object));
}
}