美文网首页
OsmAnd 底图渲染原理

OsmAnd 底图渲染原理

作者: 温温开水 | 来源:发表于2019-04-26 21:44 被阅读0次

    1.背景

    OsmAnd 使用的矢量地图
    osmdroid 使用的 栅格地图(图片展示的方式)
    瓦片地图的前世今生
    本文是探究 OsmAnd -- 矢量地图 渲染底图的方式

    2.基础知识

    Android中的SurfaceView使用简介

    3. 源码分析

    3.1 准备图片

    绘制入口 mapView.refreshMap(true);

    // this method could be called in non UI thread
        public void refreshMap(final boolean updateVectorRendering) {
        //此处的 view 是Activity里的 OsmAndMapSurfaceView
            if (view != null && view.isShown()) {
                boolean nightMode = application.getDaynightHelper().isNightMode();
                Boolean currentNightMode = this.nightMode;
                boolean forceUpdateVectorDrawing = currentNightMode != null && currentNightMode != nightMode;
                if (forceUpdateVectorDrawing) {
                    resetDefaultColor();
                }
                this.nightMode = nightMode;
                DrawSettings drawSettings = new DrawSettings(nightMode, updateVectorRendering || forceUpdateVectorDrawing);
                sendRefreshMapMsg(drawSettings, 20);
                refreshBufferImage(drawSettings);
            }
        }
        
        private void refreshBufferImage(final DrawSettings drawSettings) {
            if (mapRenderer != null) {
                return;
            }
            if (!baseHandler.hasMessages(BASE_REFRESH_MESSAGE) || drawSettings.isUpdateVectorRendering()) {
                Message msg = Message.obtain(baseHandler, new Runnable() {
                    @Override
                    public void run() {
                        baseHandler.removeMessages(BASE_REFRESH_MESSAGE);
                        try {
                            DrawSettings param = drawSettings;
                            Boolean currentNightMode = nightMode;
                            if (currentNightMode != null && currentNightMode != param.isNightMode()) {
                                param = new DrawSettings(currentNightMode, true);
                                resetDefaultColor();
                            }
                            if (handler.hasMessages(MAP_FORCE_REFRESH_MESSAGE)) {
                                if (!param.isUpdateVectorRendering()) {
                                    param = new DrawSettings(drawSettings.isNightMode(), true);
                                }
                                handler.removeMessages(MAP_FORCE_REFRESH_MESSAGE);
                            }
                            refreshBaseMapInternal(currentViewport.copy(), param);
            //此处绘制 surfaceView,将准备好的 Bitmap 绘制到 surfaceView 上
                            sendRefreshMapMsg(param, 0);
                        } catch (Exception e) {
                            LOG.error(e.getMessage(), e);
                        }
                    }
                });
                msg.what = drawSettings.isUpdateVectorRendering() ? MAP_FORCE_REFRESH_MESSAGE : BASE_REFRESH_MESSAGE;
                //baseHandler 是一个异步线程 handler
                baseHandler.sendMessage(msg);
            }
        }
    

    执行方法为 refreshBufferImage --> refreshBaseMapInternal

    private void refreshBaseMapInternal(RotatedTileBox tileBox, DrawSettings drawSettings) {
            long start = SystemClock.elapsedRealtime();
            final QuadPoint c = tileBox.getCenterPixelPoint();
            Canvas canvas = new Canvas(bufferBitmapTmp);
            fillCanvas(canvas, drawSettings);
            //layer 是具体执行绘制的类
            for (int i = 0; i < layers.size(); i++) {
                try {
                    OsmandMapLayer layer = layers.get(i);
                    canvas.save();
                    // rotate if needed
                    if (!layer.drawInScreenPixels()) {
                        canvas.rotate(tileBox.getRotate(), c.x, c.y);
                    }
                    //所有绘图的逻辑入口
                    layer.onPrepareBufferImage(canvas, tileBox, drawSettings);
                    canvas.restore();
                } catch (IndexOutOfBoundsException e) {
                    // skip it
                    canvas.restore();
                }
            }
            Bitmap t = bufferBitmap;
            synchronized (this) {
                bufferImgLoc = tileBox;
                bufferBitmap = bufferBitmapTmp;
                bufferBitmapTmp = t;
            }
            long end = SystemClock.elapsedRealtime();
            additional.calculateFPS(start, end);
        }
    

    上述代码执行完后即准备好地图底图,具体的绘制逻辑在 layer.onPrepareBufferImage

    3.2 将图片显示在界面上

    refreshBaseMapInternal后会执行 sendRefreshMapMsg(param, 0);

    
        private void sendRefreshMapMsg(final DrawSettings drawSettings, int delay) {
            if (!handler.hasMessages(MAP_REFRESH_MESSAGE) || drawSettings.isUpdateVectorRendering()) {
                Message msg = Message.obtain(handler, new Runnable() {
                    @Override
                    public void run() {
                        DrawSettings param = drawSettings;
                        handler.removeMessages(MAP_REFRESH_MESSAGE);
                        refreshMapInternal(param);
                    }
                });
                msg.what = MAP_REFRESH_MESSAGE;
                //该handler为主线程handler
                if (delay > 0) {
                    handler.sendMessageDelayed(msg, delay);
                } else {
                    handler.sendMessage(msg);
                }
            }
        }
    

    该 msg 会 执行 refreshMapInternal -->drawOverMap,drawOverMap 即是将 3.1 准备的 bitmap 绘制到 view 上。

    public void drawOverMap(Canvas canvas, RotatedTileBox tileBox, DrawSettings drawSettings) {
            if (mapRenderer == null) {
                fillCanvas(canvas, drawSettings);
            }
            final QuadPoint c = tileBox.getCenterPixelPoint();
            synchronized (this) {
                if (bufferBitmap != null && !bufferBitmap.isRecycled() && mapRenderer == null) {
                    canvas.save();
                    canvas.rotate(tileBox.getRotate(), c.x, c.y);
                    //将 bufferBitmap 绘制到 surfaceView 上
                    drawBasemap(canvas);
                    canvas.restore();
                }
            }
    
            if (onDrawMapListener != null) {
                onDrawMapListener.onDrawOverMap();
            }
    
            for (int i = 0; i < layers.size(); i++) {
                try {
                    OsmandMapLayer layer = layers.get(i);
                    canvas.save();
                    // rotate if needed
                    if (!layer.drawInScreenPixels()) {
                        canvas.rotate(tileBox.getRotate(), c.x, c.y);
                    }
                    if (mapRenderer != null) {
                        layer.onPrepareBufferImage(canvas, tileBox, drawSettings);
                    }
                    layer.onDraw(canvas, tileBox, drawSettings);
                    canvas.restore();
                } catch (IndexOutOfBoundsException e) {
                    // skip it
                }
            }
            if (showMapPosition || animatedDraggingThread.isAnimatingZoom()) {
                drawMapPosition(canvas, c.x, c.y);
            } else if (multiTouchSupport.isInZoomMode()) {
                drawMapPosition(canvas, multiTouchSupport.getCenterPoint().x, multiTouchSupport.getCenterPoint().y);
            } else if (doubleTapScaleDetector.isInZoomMode()) {
                drawMapPosition(canvas, doubleTapScaleDetector.getCenterX(), doubleTapScaleDetector.getCenterY());
            }
        }
    

    3.3 矢量地图绘制原理总结

    OsmAnd 的底图是由一层层 layer 绘制而成,先准备好 bitmap ,通过 drawBasemap(canvas); 来完成最终的展示

    相关文章

      网友评论

          本文标题:OsmAnd 底图渲染原理

          本文链接:https://www.haomeiwen.com/subject/gkrcnqtx.html