I am working on a hiking app. The end user sometimes wanna see the entire route, which could span 5-600 km in total, or he could zoom in to a special area for seeing how to follow a trail.
The test data I use right now, uses 18.000 points for the route - this is kinda "heavy" for the MapsUI engine, so I got this idea.
What if I created a MemoryLayer for each zoom level, and then skipped some points for the route on the more zoomed out layers.
So, if user looks at the entire route, instead of 18.000 points, that layer only shows maybe 1800 points (a more rough version of the route).
The next layer, which is the next zoom level, uses a little more points for the route, and os it continues.
The most zoomed in layer, has all the details of the route, but as the points are only rendered in the viewport, it performs quite snappy.
But, my problem is that all MemoryLayers are rendered, no matter what I put into MinVisible and MaxVisible ???
So when I in the end has zoomed in, I can see all layers at the same time :

I think Charlenni wrote somewhere that MemoryLayers does not support min/max visible ?
Is there a simple way to add this functionality ?
The logic to decide, if a layer is rendered or not, looks like this:
if (layer.Enabled == false) continue;
if (layer.MinVisible > viewport.Resolution) continue;
if (layer.MaxVisible < viewport.Resolution) continue;
So I would say, you use the wrong values for MinVisible/MaxVisible. What values do you use?
Yes, and that code is inside the "VisibleFeatureIterator" class.
I use the resolution values, so they should be okay - if I debug the "VisibleFeatureIterator" class, it also only renders 1 of my MemoryLayers, depending on the zoom level.
But even so, they are all visible!
Btw. why don't you write your own DataProvider and implement IEnumerable<IFeature> GetFeaturesInView(BoundingBox box, double resolution) in a way, that for a given resolution the number of max. points in a LineString are limited to a given number or return only features with points lying inside the box. These means, if the found feature in the box has to many points, create a new one and reduce the number of points.
What I have done now, which seems to work flawlessly, is to use Layer instead of MemoryLayer, and then add my lines (Linestring) as a MemoryProvider, and settings the MinVisible/MaxVisible values again.
That seems to work, and speed is super cool.
But interesting approach you suggest too!
Do you have an example? I don't think, that this should happen, because the VisibleFeatureIterator should be the onla place, where layers are drawn.
I only have that inside my app code currently, but I created a "test project" yesterday - I could try implementing the same code there.
That would be great.
Working on it right away!
Weird, don't understand how the MemoryLayer could be different since the MinVisible/MaxVisible is determined by the VisibleFeatureIterator. Are you sure you are not sharing data of several layers in the same layer? Do they share the same DataProvider?
I agree, and while debugging I also saw that only the layers within the correct zoom values got a call to .Render().
I cannot say if I did code something wrong - now I am trying to add the same to my test project, and if that "fails" too, I will package a version for you to download.
I have a test project for you to test now : https://www.dropbox.com/s/x3zcs4ipk56relx/mapsuitest.zip?dl=0
It creates a MemoryLayer for each zoom level, adds a random Polyline with its own color per layer.
You can click the "Debug layers" button to have it debug each layer on the map.
As you can see, all layers are visible despite the MinVisible and MaxVisible settings!
That was a easy one ;-)
If you use Polyline, than you use MapView. MapView saves all Drawables (and Polyline like Polygon is one) in a special layer. This layer isn't depended from min/max zoom levels and works complete indepedent from other layers you add direct to Map. MapView is a view, that should mimic the Xamarin.Forms original maps component.
If you want to use normal Mapsui features, than you should use LineString for this. Than you could do all by your own. But be carefull, because Mapsui uses another coordinate system than MapView.
But even than you would get two MemoryLayers by the time, because the min zoom level and the max zoom level of another MemoryLayer is the same. Both would be displayed.
Thanks for you time looking at the project - appreaciate it.
So what you are saying is that even though I add 10 MemoryLayers to the Layers collection, they are being add to the same Mapview drawables collection ?
What happens to the MemoryLayers then ? They are still there, so ... ?
But yes, that might explain the "bad" performance.
Earlier today I did the change at used LineString on different layers with their own seperate MinVisible and MaxVisible settings, and it works as expected.
The MemoryLayers are empty. They couldn't handle any Polyline. You create only Drawables, which all go into a layer for Drawables behind the scenes. That's the magic of MapView.
Your code should look like this
public MemoryLayer CreateZoomLayer(int zoomLevel)
{
MemoryLayer ml = new MemoryLayer();
var pl = new List<Mapsui.Geometries.Point>();
pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui());
pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui());
pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui());
pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui());
var line = new LineString();
line.Vertices = pl;
var data = new MemoryProvider(line);
ml.DataSource = data;
ml.Style = new VectorStyle() { Line = new Pen { Color = GetZoomColor(zoomLevel).ToMapsui() } };
ml.MinVisible = GetZoomResolution(zoomLevel + 1);
ml.MaxVisible = GetZoomResolution(zoomLevel);
ml.Name = "RoutesLayer_" + zoomLevel;
return ml;
}
You could try to set the min/max by hand in Polylines. Try
((VectorStyle)pl.Feature.Styles.First()).MinVisible = GetZoomResolution(zoomLevel + 1);
((VectorStyle)pl.Feature.Styles.First()).MaxVisible = GetZoomResolution(zoomLevel);
in your code. That should do, what you want.
@charlenni. The MapView makes it easy for users by letting them work with gps coordinates. This is appreciated. This is something they understand and they get confused by talk about coordinate systems. The solution is now to add a custom Polyline, Polyin. So a lot of duplicate implementation with it's own issues. Would it perhaps be an alternative to add helper methods to convert gps coordinates to Mapsui polygons and linestrings?
public MemoryLayer CreateZoomLayer(int zoomLevel) { MemoryLayer ml = new MemoryLayer(); var pl = new List<Mapsui.Geometries.Point>(); pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui()); pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui()); pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui()); pl.Add(new Position(40.8 + GetRandomShift(), -8.9 + GetRandomShift()).ToMapsui()); var line = new LineString(); line.Vertices = pl; var data = new MemoryProvider(line); ml.DataSource = data; ml.Style = new VectorStyle() { Line = new Pen { Color = GetZoomColor(zoomLevel).ToMapsui() } }; ml.MinVisible = GetZoomResolution(zoomLevel + 1); ml.MaxVisible = GetZoomResolution(zoomLevel); ml.Name = "RoutesLayer_" + zoomLevel; return ml; }
That is exactly how I rewrote my code inside the app yesterday. Thanks!
Before you change your code: do you test the other way I mentioned?
I rewrote yesterday before I got your answer, so probably not ?
What way are you referring to ?
((VectorStyle)pl.Feature.Styles.First()).MinVisible = GetZoomResolution(zoomLevel + 1);
((VectorStyle)pl.Feature.Styles.First()).MaxVisible = GetZoomResolution(zoomLevel);
Ohhh, let me just try that - thanks!
((VectorStyle)pl.Feature.Styles.First()).MinVisible = GetZoomResolution(zoomLevel + 1); ((VectorStyle)pl.Feature.Styles.First()).MaxVisible = GetZoomResolution(zoomLevel);
It works as expected - great, thanks!
Would it perhaps be an alternative to add helper methods to convert gps coordinates to Mapsui polygons and linestrings?
There are already helper methods to convert coordinates to Mapsui. Under the hood, MapView already uses the LineString as Feature. Poly line is only a wrapper around the Feature.
It works as expected - great, thanks!
How about the speed?
Perfect. If I find some time, I try to make a property for this and perhaps for Pins too.
I only tested with my simple random lines per layer, but speed seems okay!
Ok, if this is solved now, could you close the issue?