I have big KML file to a native Android application, please check the following details and give an advice.
KML file details:
size: 1.7 MB
total number of kml file elements: 500 elements
total number of polygon: 1000 polygon
Android app details:
the above details will be viewed in Fragment
I used the following support library to implement this screen
compile 'com.google.maps.android:android-maps-utils:0.4+'
some caluctations are done on loading the screens(like distance calculations)
Issue:
Take a lot of time to load the map and kml layer about 8 sec
create KMLLayer instance
What is the best practice to implement the above details with good performance?
please advise.
Best practice is doing long time operation in background (for example, on separate thread) and split complex tasks into small parts. So you can:
1) create and start load KML layer as soon as possible (e.g. on app create) and than just show it;
2) instead of one kml file with 500 elements and 1000 polygons, use 50 kml files with 10 elements and 100 polygons and load to layer only necessary files (for example you can split it by area location, or by semantic information, or by something else criteria);
3) combine 1 and 2 points;
4) precisely for google maps it's possible to create tiles with information from kml files and use TileProvider.
Since there's no actual answer, I'll post my own solution.
I've done mainly two things to optimize this.
Instead of using Google maps utils built-in method addLayerToMap - I've used my own custom implementation. I've done so, because parsing kml is relatively fast(in my case - ~5-10 seconds) and it can be done in background thread. Adding all the points to the map, however, takes more than 10 seconds, and must be done on UI thread. Basically, I've used KmlLayer class as a base, and parsed kml file by myself into polygonOptions and all other things I need.
Calculate which items are visible and which are not. Firstly, I filter which items are outside screen bounds. After that, I calculate each item's size(in pixels, no meters) - if item is smaller than threshold - item is also filtered out.
With these hacks applied, instead of freezing app for 15 seconds, user can freely navigate through map, and when he stops, after several seconds information will be displayed.
I just make some updates on the screen behavior to get good performance and good user experience by the following steps:
Divide the KML File to 65 files (the main areas on the map, for example, Zone A11 is located in one KML file and it contains all its details like Zone A11-1, Zone A11-2 and Zone A11-4 ...) and this division is done to be suitable the screen experience (for example user requirements)
on the first load, I am loading only the markers for all KMLs centers and that is not affecting the performance
When user click on the marker, I am loading the KML file for this area and zoom this area
When user zoom out, I am removing this layer from the map and load marker
When user moves the map I am calculating the nearest marker and load its KML layer
Note: preparing files on app launching will not provide better performance because the bad performance comes from adding the KML layer to the google map
Note2: using custom implementation for parsing and adding to Google map take a lot of time or need a lot of unit testing, and I think it's not recommended solution because it's better to leave this solution to be on Google Map utils build-in method (it's customized and always up to date)
I'd suggest you to make sure that you are constructing the KmlLayer on a background thread and addLayerToMap() has to be done on the main thread though like this
GlobalScope.launch {
val stringUrl: String = "https://saltwx.com/bath/SW_bath6.kml"
val inputStream: InputStream = URL(stringUrl).openStream()
val layer = KmlLayer(map,inputStream, applicationContext)
runOnUiThread {
try {
layer.addLayerToMap()
} catch (e: Exception) {
e.printStackTrace()
}
}}
Related
I'm creating an Android app using MapBox. I've already set up a simple map functionality with markers sourced from .json file. Next step is filtering the markers on the map, just like in this gl-js example here:
https://docs.mapbox.com/mapbox-gl-js/example/filter-markers/
I can't find any sdk examples anywhere, and since this is my first app I really can't figure it out on my own. Any help will be appreciated.
You can check out this example https://docs.mapbox.com/android/maps/examples/multiple-expressions-temperature-change/ that features two layers that fetch a min or max temperature from the data source and display it.
The filtering part is done here:
// Only display Maximum Temperature in this layer
maxTempLayer.setFilter(eq(get("element"), literal("All-Time Maximum Temperature")));
loadedMapStyle.addLayer(maxTempLayer);
Filters accept expressions as arguments, and here Expression.eq is used to compare "element" from the GeoJSON data source (referenced with the Expression.get) with the "All-Time Maximum Temperature" value. If it resolves to true, the feature is going to be displayed, otherwise, it's going to be hidden.
I use here-map sdk. I have db file with 16500 ! paths (coordinates of a point). I need to draw all paths on the map, when user activate function "show additional paths". But i think, if i try to fetch big number of path and add all poplilynes object on here map, it will take a huge amount of time.
Help to find the optimal solution.
I would filter your data based on the visible viewport and disable this functionality where it doesn't make much sense (continental or globe level).
So, let's assume you app shows the map on zoomlevel 16 or 17 (district level), you can retrieve the viewport as GeoBoundingBox from the Map instance (e.g. via mapView.getMap()) with getBoundingBox().
The GeoBoundingBox makes it easy for you now to check for collisions with your own objects, since it has several "contains()" methods.
So everything that collides with your viewport should be shown, everything else is ignored.
You can update whenever the map viewport changes with either listening for OnTransformListener in the Map class or register for the MapGesture events (get MapGesture via getMapGesture() and listen for zooming events via addOnGestureListener())
If the amount of data for filtering is still too big, you can also think about preparing your data for more efficient filtering, like partitioning (region based would be my first idea) so only a subset of your data needs to be filtered.
It seems that Custom Location Extension (https://developer.here.com/platform-extensions/documentation/custom-location/topics/what-is.html) can help with this case.
In short, it allows you to upload a custom data to the HERE backend and query it later.
I am trying to save route using SkRouteManager's saveroutetocache, and showing route using loadroutefromcache but actually the route is not being saved so every time I load it from the stored id, it shows the route cannot be calculated.
Is there any way to save the route not just in the current map view?
Check out the dedicated chapter for route caching/loading in the documentation and also see the implementation in the demo project (you will have to make sure that "provideMultipleMapSupport" is true in the demo project's manifest for the multiple map views code to be active - see this for details)
I'm writing an app to track movements on google maps v2. For each position changed, I'm adding a new map point to my array, then adding a polyline to the map. I'm also saving the location in a sqlite database. Here is the relevant code:
LatLng mapPoint = new LatLng(location.getLatitude(), location.getLongitude());
routePoints.add(mapPoint);
Polyline route = map.addPolyline(new PolylineOptions().color(Color.BLUE).width(2));
route.setPoints(routePoints);
After about 2000 points, the app becomes unresponsive on my phone. I don't think it's because the array is getting too big because when I pull all of the data from the database (sometimes over 6000 rows), it follows the same logic and paints the map just fine (using the array). I'm wondering if it's because I have everything running on the main thread (music playing, google map, location services, database inserts, textview changes etc). If that's the culprit, how should I change this up to put things in a different thread? What should go in the different thread? And lastly, how would I write up moving any of these things to a different thread (code samples or point me to a resource).
TIA
Ideally you'll want to move everything that doesn't involve the UI into other threads, especially networking and file-accessing (e.g. database) codes. It's probably a death by a thousand cuts situation here. A few suggestions:
Definitely move database/any file access to another thread using AsyncTask and notify your UI to refresh when data have been loaded (confirm you've done this by enabling StrictMode
Minimize database access, since it's backed by flash storage, the same operation can sometimes take 1ms and other times 50ms due to other apps/Android OS itself accessing storage. Once you reads a row, caches it.
If you are using MediaPlayer, either move it to an AsyncTask or call its asynchronous methods (e.g. prepareAsync())
It's also possible that you are issuing too many drawing commands, do some test to see if this is the case. If it is, then only issue draw commands when you know it'll be displayed on screen.
You'll probably want to make your changes in the order presented above.
I don't really know what you are trying to accomplish, but here's a rough outline of how to use AsyncTask:
private class LocationTask extends AsyncTask<Source, Integer, List<PolylineOptions>> {
protected Long doInBackground(Source... sources) {
List<PolylineOptions> list=new ArrayList<PolylineOptions>();
//create or retrieve Polyline objects here
return list;
}
protected void onProgressUpdate(Integer... progress) {
//don't need this if it's reasonably fast
}
protected void onPostExecute(List<PolylineOptions> result) {
for(PolylineOptions poly:result) {
map.addPolyline(poly);
}
}
}
To run: new LocationTask().execute(source1, source2, source3);
Source is whatever data structure you use to give LocationTask the ability to perform its function
i'm starting to developt a android game using AndEngine gles2, it's a really good game engine and i found a lots of example of how use it. My game is a kind of RPG traditional game using tiled maps, sprite, etc.: my issue is how i can set a mounster respawn area(like the long grass areas on pokemon games) i read a lots of tile map example but in no one appears examples about this, i using a tiled map editor and there i can set properties to the layers and especify there if is a mounster respawn area or not, if can someone tell me how can i do this i will appreaciate so much.
Are you using the tilemapeditor found here:
http://www.mapeditor.org/
(note, you need to save it with the correct compression type or otherwise andengine will not parse it correctly, cant remember which one I got working but it takes a few minutes to try out).
Then you can rightclick on a specific tile and add properties to it such as:
IsMonsterSpawnArea value = true
Then you can check for "collision" with
if(pTMXTileProperties.containsTMXProperty("IsMonsterSpawnArea ", "true")) {
// SPAWN BIG UGLY MONSTER(or initiate fight!)
}
Check out the andengine TMX examples found in the andengine source, they can really help you get started.