Slow updating HereMaps DomIcon when location (lat / long) changes, re-render - javascript

Using renderToStaticMarkup to render html to a DomIcon in HereMaps. That's working and quick when there's not that many markers and updates. However, 100 markers frequently updating and re-rendering is causing a slow page open. It's even slowing the map render.
I've looked into best practices around re-using a DomIcon. I've also been looking into clustering -- but not sure how updating would work. Is clustering the only way to go further here? Curious if there's any other best practices for performance

Clustering solves the potential performance issues, however H.map.Group is used to associate the markers together and the group.getBounds() method to find the minimal bounding box holding all of the group's contents. The map.viewBounds() can then be updated.
function addMarkersAndSetViewBounds() {
// create map objects
var toronto = new H.map.Marker({lat:43.7, lng:-79.4}),
boston = new H.map.Marker({lat:42.35805, lng:-71.0636}),
washington = new H.map.Marker({lat:38.8951, lng:-77.0366}),
group = new H.map.Group();
// add markers to the group
group.addObjects([toronto, boston, washington]);
map.addObject(group);
// get geo bounding box for the group and set it to the map
map.getViewModel().setLookAtData({
bounds: group.getBoundingBox()
});
}
forEachDataPoint (callback)
This method invokes the specified callback for each data point in the given cluster which can work for updating the data points.
A clustering algorithm groups data points by collapsing two or more
points positioned close to one another on the screen into a single
cluster point. All other (not collapsed) points are still visible on
map as noise points.
If this actually suits the use case, go for clustering. It helps to uplift the performance issue. for more details refer :
developer.here.com/documentation/maps/topics/clustering.html

Related

Detect when user reaches maxBounds using Leaflet

I am using leaflet to show an interactive map to our users.
We want to let them browse through a limited area, and inform them they have to subscribe in case they want to see something too far away (using a pop up or equivalent).
So far I have seen that Leaflet supports a maxBounds option.
This is a good start that lets me prevent users to see larger areas.
Now I would like to be able to detect a maxBounds 'event' to show the user a pop up.
I have been looking into the Leaflet source code, but couldn't find an obvious way to do it.
so far I have found that the maxBounds option is fed into the setView method.
This method itself uses the _limitCenter method to define the center.
This goes a few levels deeper, down to the _getBoundsOffset method that finally uses the bounds.
_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
var projectedMaxBounds = toBounds(
this.project(maxBounds.getNorthEast(), zoom),
this.project(maxBounds.getSouthWest(), zoom)
),
minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
dx = this._rebound(minOffset.x, -maxOffset.x),
dy = this._rebound(minOffset.y, -maxOffset.y);
return new Point(dx, dy);
},
The closest I could find so far would be to hook into the moveend event and check whether the center is out of my bounds manually.
However, it seems like this would be redundant with what leaflet is already doing.
Is there a better to leverage leaflet to achieve this?
Thanks
Just check if your defined bounds contain the map bounds. As long as the map bounds are inside the defined bounds, this will do nothing:
var myBounds = L.latLngBounds(...)
map.on('move moveend zoomend', function(){
if (!myBounds.contains(map.getBounds())) {
// Display popup or whatever
}
});
it seems like this would be redundant with what leaflet is already doing.
Don't worry about that. The overhead is negligible for this use case.

how to improve performance on inserting a lot of features into a map with leaflet.js

With the prototype code below I'm adding a lot of features in steps into an osm.
I'm loading about 8500 multipolygon features into it. Some of them have a lot of coordinates so that's round about 150MB of textual data in total.
Loading them one by one leads to a crash of the browser. Loading it in chunks works but it is not fast either. Especially if you want to scroll or zoom after the loading has finished.
I'm a bit shy about loading it all in one go as that's 150MB of data.
What options do I have to improve the experience? To be clear: I'm not talking about the loading itself. I'm talking about the rendering of the map with the features.
Here is the code stub:
addToMap = function (id, totalCount) {
var idTo = id+99;
jQuery.get('getData.php', {id: id, idTo: idTo}, function (result) {
var geojson;
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature
});
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
geojson = L.geoJson(result, {
style: getStyle,
onEachFeature: onEachFeature
}).addTo(map);
if (id < totalCount) {
jQuery('#count').html(idTo+' of '+totalCount);
addToMap(idTo+1, totalCount);
} else {
jQuery('#loader').remove();
}
}, 'json');
}
The secret to render a lot of stuff very very fast is... to not render a lot of stuff.
This might seem contradictory, but in reality it's very simple. You don't need to render everything, you just need to render:
Stuff which is inside the screen (plus a bit of an out-of-the-screen margin)
Stuff which measures less than one pixel (because nobody will ever notice subpixel artifacts)
By default, Leaflet does in fact simplify the vector geometries to save some time (douglas-peucker up to a couple of pixels), but it simplifies all the geometries (which is computationally expensive) and renders based only in the geometries' bounding boxes (which renders big geometries which are not visible, and renders all the points of big geometries for which only a tiny bit is visible).
Fortunately a couple of recent developments help with that: vector tiles and geojson-vt. Do read https://www.mapbox.com/blog/introducing-geojson-vt/
The general idea is that the dataset is subject to a precomputation step (which takes a non-trivial amount of time but can be done off-thread), slicing the data up into tiles. Slicing in tiles means that only the visible part of big geometries will be shown, saving huge amounts of time. It will also run some line simplification, depending on the level of the tile pyramid.
These map tiles follow the same standard than raster tiles, so the visibility algorithms can be shared around.
To the best of my knowledge, there is only one working implementation of geojson-vt and Leaflet: Leaflet.VectorGrid (or you can check the plugins list, which might contain more related plugins in the future). I suggest you have a look at it.
In addition to the other answers:
Convert your GeoJson to TopoJson to reduce the size of it. Here is one utility to do it - https://github.com/topojson/topojson
Based on your zoom level, display only parts of the features that are important or large enough. (as IvanSanchez wrote)
Well, how much of your stuff needs to be a feature? I'm getting around the same problem by rendering features as raster map tiles with transparency and then displaying them in a stack. This way users can still switch off certain things. Since you're rendering them from vector, it will still look great at any zoom level. It's also far more efficient!

Assign feature layer properties assigned to counties to a division ( group of counties)

Below written code highlights a county when a mouse is moved on to a particular county. I want to highlight the entire division(set of counties) when the mouse is moved on to any county of that particular division..
map = new Map("mapDiv", {
basemap: "streets",
center: [-96.921387,36.084621],
zoom: 8,
slider: false
});
var oklahomaCounties = new FeatureLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/3",
{
//mode: FeatureLayer.MODE_SNAPSHOT,
outFields: ["NAME"]
});
oklahomaCounties.setDefinitionExpression("STATE_NAME = 'Oklahoma'");
I found a sample I guess it's seems similar to your need.
http://developers.arcgis.com/javascript/sandbox/sandbox.html?sample=fl_hover
Hope this will help you :)
If you need working solution please share exact code where your are facing issue.
Edited:
Based on below discussion below are the steps to achieve it.
We have two way to achieve it.
First Approach:
Onhover; I fetch all the division records from given layer and add it to the map but this will cause you performance issue like after hovering you have to wait utill you get all the records.
Second Approach
Initially on load of app get all records from above layer, this will be one time activity; process the output data/features and create collection in such a manner so that key will be division name and features push to its specific division. once you are ready with collection then simply on hover which ever feature you are getting first find from which group/division its belonging and highlight all the feature from that division on the map.
I would recommend to go with this because it will not fetch every time data; only first load time will increased a little bit.
Let me know if any clarifications required.

Displaying Antarctica using GeoJSON in heremaps

I'm trying to render Antarctica geojson shape on a map using the HERE maps api.
The geojson is found here: https://github.com/johan/world.geo.json/blob/master/countries/ATA.geo.json
You can see github renders it nicely.
Using the same geojson on geojson.io also renders it nicely.
But somehow it seems to render the 'inverse' of Antarctica when using it in HERE maps.
It colors everything except antarctica.
see: http://imagebin.ca/v/1dZIn5vsEuFx
(I've tried making an expample using jsfiddle, but it's not able to load external json. And the HERE maps api doesn't allow you to load geoJSON from a string)
Is there an issue with the geoJSON? Is there an issue with the HERE maps api?
The API doesn't quite understand what to do with the open polygon. Because the polygon is basically just a line around the globe the API doesn't know if you shape closes over the north pole or the south pole. By default it assumes that open polygons close over the north pole. You can change this by using this flag (setNorthPoleCovering):
http://developer.here.com/javascript-apis/documentation/v3/maps/topics_api_nlp/h-map-polygon.html#h-map-polygon__setnorthpolecovering
However, actually getting to that point in the code where this can be done is a bit complicated:
// When you instantiate the geojson.Reader you can specify a function that
// receives all objects the reader parsed. It is called when objects are
// being rendered on the map. At that point we can look into the object and
// check whether it is Antarctica
var reader = new H.data.geojson.Reader('...ATA.geo.json', {
style: function(obj) {
if (obj.getData().properties.name === "Antarctica") {
//AHA! We found Antarctica!
// Since this is a multi-polygon we have a group here which contains
// all polygons. We apply the north-pole-covering flag to each of the
// polygons
obj.forEach(function(polygon) {
polygon.setNorthPoleCovering(false);
});
}
}
});
reader.parse();
map.addLayer(reader.getLayer());
Depending on what you want to accomplish in terms of dynamic behavior, if you are just looking to display or share a map with cards and other metadata about a country with some basic styling -- HERE XYZ can be used to render GeoJSON on a HERE map.
If you want to do it with JavaScript rather than an embedded iframe, the other answer may be what you are looking for.
There is an there an issue with the GeoJSON, and other mapping APIs would have the same problem. It needs to be closed at the 180th meridian, so
[178.277212,-84.472518],[180,-84.71338],[-179.942499,-84.721443]
becomes
[178.277212,-84.472518],[180,-84.71338],[180,-90],[-180,-90],[-180,-84.71338],[-179.942499,-84.721443]

If current bounds lie in others (many) bounds?

I am using google maps v3.
I want to know IF current map bounds lies in a sum of all previous bounds (all points of current bounds are covered by other bounds) (if not, I will load markers for current bounds via ajax)
Let say, I have an array of previous viewport bounds, that I do so:
google.maps.event.addListener(map, 'idle',function() {
var bounds = map.getBounds();
boundsArray.push(bounds);
});
The algorithm to check that I invented by now is a hard one and it is the following:
1) Generate all edge points (lan/lon) of current bounds with some $step (0.0001?) and put them in "pointsArray".
2) Iterate over pointsArray, and check if current point exists in at least one previous bounds:
From LatLngBounds doc: contains(latLng:LatLng) boolean Returns true if the given
lat/lng is in this bounds.
so, something like:
if (boundsArray[y].contains(pointsArray[i]) {
and if contains, then remove that point from pointsArray
3) Finally, after all iterations, if the pointsArray is empty — it means all points of current bounds are inside another/s bounds and function returns true...
There are in my opinion 2 disadvantages of my algorithm:
1) it is not 100% precise (depends on $step)
2) and that is worse, it my cause performance drop on a client, checking so many points by all bounds... as javascipt is implemented on users PC.
So, if the any more precise & easy, faster solution for my problem? maybe using another math approaches, google geometry library & so on? How to do that correctly?
You can delete the bounds array when it becomes too big. Then you can start again. It's much easier then to compare the bounds.

Categories

Resources