I have a temperature map for some area with 3 km grid. Ie I have about few thousands of polygons with color.
I'm trying to show them on Google Map on android in my kotlin application.
The issue is that I need to add GeoJson layer in UI thread and it takes 8-15 seconds. Ie all this time the application is frozen
There are a lot of old answers on StackOverflow about this issue but they all are not relevant for me:
I tried:
Still I have more or less the same time when UI is frozen
How can I manage this issue? Is there any way to create a map from thousands of colorful rectangles in a background thread and then show it immediately in UI thread?
If you already try addPolygon()
and GroundOverlay
there are two possibilities left:
using Tile Overlays (preferred);
using custom drawing over MapView
or MapFragment
.
IMHO Tile Overlay is a better way due possibility of high performance TileProvider
implementation. For example, you can create tiles for "low" zoom levels and "current" (level that should be shown to user at the beginning) zoom level and store them in array ( HashMap
, etc.) or file system path ..\\zoom_level\\x\\y\\tile.png
if there are a lot of tiles. And more "detailed" tiles you can create "on the fly" (in separate thread) when it needs to be shown, and then also store them for future using (if needed). Of course, you need custom module for fast GeoJson
reading (something like Jackson) and rendering it to the .png
tiles. So, seems it is possible to create TileProvider
for your case, optimal by performance and memory consumption. You can use this answer of Alex Vasilkov as first iteration.
If you choose custom drawing you should override onDraw()
method for MapView
or dispatchDraw()
for MapFragment
. like in this answer . In that case you can control all of the process, but that way is more complex for implementation.
Update:
You can implement action for onCameraMove()
, like in this answer (some tricky passing of GoogleMap
object used there):
public class RadarMapView extends MapView implements OnMapReadyCallback { private OnMapReadyCallback mMapReadyCallback; private GoogleMap mGoogleMap; private Marker mMarker; private Paint mPaintRadar; public RadarMapView(@NonNull Context context) { super(context); init(); } public RadarMapView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public RadarMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public RadarMapView(@NonNull Context context, @Nullable GoogleMapOptions options) { super(context, options); init(); } @Override public void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); canvas.save(); drawRadarOverTheMap(canvas); canvas.restore(); } private void drawRadarOverTheMap(Canvas canvas) { if (mGoogleMap == null) { return; } final float centerX = getX() + getWidth() / 2; final float centerY = getY() + getHeight() / 2; canvas.drawCircle(centerX, centerY, 150, mPaintRadar); canvas.drawCircle(centerX, centerY, 300, mPaintRadar); canvas.drawCircle(centerX, centerY, 450, mPaintRadar); } private void init() { setWillNotDraw(false); mPaintRadar = new Paint(); mPaintRadar.setColor(Color.GREEN); mPaintRadar.setStyle(Paint.Style.STROKE); mPaintRadar.setStrokeWidth(10); } @Override public void getMapAsync(OnMapReadyCallback callback) { mMapReadyCallback = callback; super.getMapAsync(this); } @Override public void onMapReady(GoogleMap googleMap) { mGoogleMap = googleMap; mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { @Override public void onCameraMove() { invalidate(); // NB! Exactly this line you need } }); if (mMapReadyCallback != null) { mMapReadyCallback.onMapReady(googleMap); } } }
Update #2:
You can make "screenshot" (not exactly screenshot, but create image of polyigons on the bitmap) of current view of polygons and move it in onCameraMove()
(not redraw all polygons). And then in onCameraIdle()
create and show new full polygons view. Also, you can create bitmap slightly bigger then map screen view (for zooming out and scrolling properly). Or you can "skip" some of the onCameraMove()
calls (eg call invalidate()
once per 3 onCameraMove()
calls etc.).
By the way: in case of Tile Overlays moving and zooming are available "from the box". You only need to create a tricky TileProvider. There are only several tiles need to be generated for whole device screen (size of the single tile is 256x256). So, you can generate tiles for current screen, for currentZoomLevel-1, for currentZoomLevel+1 (in case of zooming) and + 2 (or 3) tiles to the left, right, top and bottom (in case of scrolling). Also you can store generated tiles for future using in some cache (HashMap, LRU, etc.). And you can generate "extra" (not currently visible) tiles in separate threads.
i suggest to not create these few thousands of polygons one shot as this will affect time and performance but instead you can create only near polygons to shown location on the fly making use of GoogleMap.OnCameraMoveListener and GoogleMap.OnCameraIdleListener.
Edit:- "near polygons to shown location on the fly" i mean create only polygons that the user currently see ie within the current visible region boundaries of map :- you can get it by
googleMap.projection.visibleRegion.latLngBounds
"GoogleMap.OnCameraMoveListener and GoogleMap.OnCameraIdleListenerwhat", I mean you have two approaches here first, you update the map with polygons within the visible boundaries when user stop scrolling the map "setOnCameraIdleListener" or the second one is the use setOnCameraMoveStartedListener to update the map with the polygons within the visible boundaries each time the user start scrolling the map.
override fun onMapReady(googleMap: GoogleMap?) {
googleMap ?: return
with(googleMap) {
setMinZoomPreference(9f)
setOnCameraIdleListener {
// first approach user stopped scrolling so update the map with polygons within the boundaries
}
setOnCameraMoveStartedListener {
// second approach user started scrolling so update the map with polygons within the boundaries
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.