{"version":"0.1.0","code":"0000","result":true,"message":"处理成功","errdetail":"","timestamp":1671508868556,"data":{"id":71712781,"title":"6.5.2自定义图层碰撞","slug":"gksotk","format":"lake","bookId":26046811,"body":null,"body_draft":null,"body_html":"

基本介绍

基础图层自带了按优先级进行碰撞的基础碰撞能力,详细见图层碰撞。本节主要介绍如何自定义图层内的碰撞。自定义业务图层的碰撞见业务图层间碰撞

自定义图层碰撞,就是注册回调com.autonavi.gbl.map.observer.IReculateOverlay,在渲染每一帧刷新的时候会回调所有注册实现这个类的图层中。图层在recalculateOverlay函数中获取到图层图元,并根据自定义的碰撞关系实现碰撞效果。

同时需要注意的是,从550版本开始,图层的碰撞不再在引擎线程中执行(为了不影响引擎的响应效率),而是抛线程到message_bus中执行。所以要求:在用户自定义的图层碰撞中,设置图元显隐需要调用onVisible而不是setVisible,另外,图元创建后默认为不可显示(调用setVisible(false)),否则会引发闪烁问题。

场景图

\"image.png\"

\"image.png\"

时序图

关键参数

com.autonavi.gbl.map.observer.IReculateOverlay

接口

说明

void recalculateOverlay()

在RenderMap时通知图层

核心接口

//在RenderMap时通知图层\nvoid com.autonavi.gbl.map.observer.IReculateOverlay.recalculateOverlay()\n    \n//添加地图ReculateOverlay的监听句柄 可以支持多个监听,通过pIReculateOverlay句柄回调。\nvoid com.autonavi.gbl.map.MapView.addReculateOverlayObserver(IReculateOverlay pIReculateOverlay)\n\n//删除地图ReculateOverlay的监听句柄\nvoid com.autonavi.gbl.map.MapView.removeReculateOverlayObserver(IReculateOverlay pIReculateOverlay)

说明:函数详情,请复制函数名称到在线API搜索

调用示例

\npublic final class DemoConstant {\n    public static final int BusinessTypePoint = 0;\n    public static final int BusinessTypeLine = 1;\n    public static final int BusinessTypePolygon = 2;\n    public static final int BusinessTypeArrow = 3;\n    public static final int BusinessTypeCircle = 4;\n\n    public static final int BusinessTypeCollisionPoint = 5;\n}\n\nclass ItemDirection {\n    public static final int ItemDirectionLeft = 0;\n    public static final int ItemDirectionRight = 1;\n}\n\nclass PointLayerItemDemo extends PointLayerItem {\n\n    public PointLayerItemDemo(String name, int direction) {\n        mName = name;\n        mDirection = direction;\n        mCollisionArea = 0.0f;\n    }\n\n    public String mName;\n    public int mDirection;\n    public double mCollisionArea;\n}
/** @brief 定义类CollisionLayerDemo\n * 1,自定义碰撞需要继承实现IRecalcuteOverlay.recalculateOverlay,每一帧刷新时会调用该接口。\n * 2,把自己通过MapView.addReculateOverlayObserver接口添加到监听观察者中。\n * 3,该demo通过每个图元实现两个左右飘图元以及计算相互压盖面积的碰撞算法来优化显示效果,\n *\t左右飘只显示其中一个(取压盖面积最小的图元来显示)。\n */\npublic class CollisionLayerDemo extends BaseLayer implements IReculateOverlay {\n    private boolean mbAddReculateOverlayObserver = false;\n    public CollisionLayerDemo(String name, MapView mapView) {\n        super(name, mapView);\n        if (mapView != null) {\n            mapView.getLayerMgr().addLayer(this);\n        }\n    }\n\n    public void init() {\n        MapView mapView = getMapView();\n        if (mapView != null) {\n            PrepareLayerStyleDemo.getInstance().init(mapView);\n            setStyle(PrepareLayerStyleDemo.getInstance());\n            addClickObserver(LayerClickObserverDemo.getInstance());\n            setClickable(false);\n            enablePoiFilter(true);\n\n            //添加到监听观察者中\n            mapView.addReculateOverlayObserver(this);\n        }\n    }\n\n    public void unInit() {\n        MapView mapView = getMapView();\n        if (mapView != null) {\n            //从监听观察者中释放\n            mapView.removeReculateOverlayObserver(this);\n            setStyle(null);\n            removeClickObserver(LayerClickObserverDemo.getInstance());\n            enablePoiFilter(false);\n        }\n    }\n\n    /**< 图层最后不再使用是,从图层管理器中删除,同时图层会被delete释放掉 */\n    public void removeLayer() {\n        MapView mapView = getMapView();\n        if (mapView != null) {\n            mapView.getLayerMgr().removeLayer(this);\n        }\n    }\n\n    private int MAX(double x, double y) {\n        return (int)(((x) > (y)) ? (x) : (y));\n    }\n\n    private int MIN(double x, double y) {\n        return (int)(((x) < (y)) ? (x) : (y));\n    }\n\n    private double getIntersectArea(ArrayList<PixelPoint> pixelPoint1, ArrayList<PixelPoint> pixelPoint2) {\n        if (pixelPoint1.size() < 2 || pixelPoint2.size() < 2) {\n            return 0.0;\n        }\n        Rect rc1 = new Rect((int)pixelPoint1.get(0).x, (int)pixelPoint1.get(0).y,\n                (int)pixelPoint1.get(1).x, (int)pixelPoint1.get(1).y);\n\n        Rect rc2 = new Rect((int)pixelPoint2.get(0).x, (int)pixelPoint2.get(0).y,\n                (int)pixelPoint2.get(1).x, (int)pixelPoint2.get(1).y);\n\n        double xlen = 0., ylen = 0.;\n\n        if (rc1.left < rc2.left) {\n            xlen = rc1.right - rc2.left;\n            xlen = MAX(xlen, 0.);// 不相交 xlen<0\n            xlen = MIN(xlen, rc2.width());\n        } else {\n            xlen = rc2.right - rc1.left;\n\n            xlen = MAX(xlen, 0.);// 不相交 xlen<0\n            xlen = MIN(xlen, rc1.width());\n        }\n\n        if (rc1.top < rc2.top) {\n            ylen = rc1.bottom - rc2.top;\n            ylen = MAX(ylen, 0.);// 不相交 ylen<0\n            ylen = MIN(ylen, rc2.height());\n        } else {\n            ylen = rc2.bottom - rc1.top;\n\n            ylen = MAX(ylen, 0.);// 不相交 ylen<0\n            ylen = MIN(ylen, rc1.height());\n        }\n\n        return (xlen * ylen)/(rc1.width() * rc1.height());\n    }\n\n    @Override\n    public void recalculateOverlay() {\n        lockItems(); //锁住图元,保证对图元访问的线程安全\n\n        //重置为显示,压盖面积为0\n        ArrayList<LayerItem> items = getAllItems();\n        for (int i = 0; i < items.size(); i++) {\n            PointLayerItemDemo item1 = (PointLayerItemDemo)items.get(i);\n            item1.onVisible(true);\n            item1.mCollisionArea = 0.0f;\n        }\n\n        //计算两两压盖面积\n        for (int i = 0; i < items.size() - 1; i++) {\n            PointLayerItemDemo item1 = (PointLayerItemDemo)items.get(i);\n            if (item1 == null || !item1.getVisible()) {\n                continue;\n            }\n\n            ArrayList<PixelPoint> pixelPoint1 = item1.getBound();\n\n            for (int j = i + 1; j < items.size(); j++) {\n                PointLayerItemDemo item2 = (PointLayerItemDemo)items.get(j);\n                if (item2 == null || !item2.getVisible() || item1.mName.equals(item2.mName)) {\n                    continue;\n                }\n\n                ArrayList<PixelPoint> pixelPoint2 = item2.getBound();\n\n                // 相交压盖面积超过10%表示碰撞\n                double area = getIntersectArea(pixelPoint1, pixelPoint2);\n                if (area > 0.1) {\n                    item1.mCollisionArea += area;\n                    item2.mCollisionArea += area;\n                }\n            }\n        }\n\n        //根据左右飘向的压盖面积设置显隐\n        for (int i = 0; i < items.size()/2; i++) {\n            PointLayerItemDemo leftItem = (PointLayerItemDemo)getItem(i + "left");\n            PointLayerItemDemo rightItem = (PointLayerItemDemo)getItem(i + "right");\n            if (leftItem.mCollisionArea > rightItem.mCollisionArea) {\n                leftItem.onVisible(false);\n            } else {\n                rightItem.onVisible(false);\n            }\n        }\n\n        unLockItems();\n    }\n\n    public void clearAllItems() {\n        if (mbAddReculateOverlayObserver) {\n            MapView mapView = getMapView();\n            if (mapView != null) {\n                mapView.removeReculateOverlayObserver(this);//图元全部被删除时也删除注册观察者\n                mbAddReculateOverlayObserver = false;\n            }\n        }\n\n        super.clearAllItems();\n    }\n\n    //每个图元都有左右飘向,根据压盖碰撞关系决定显示左右飘向的某一个,来优化显示效果(减少压盖,显示在更合适的位置)\n    public void updatePointInfo(ArrayList<BizPointBusinessInfo> pointList) {\n        if (getMapView() == null || pointList.size() == 0) {\n            return;\n        }\n\n        clearAllItems();\n        MapView mapView = getMapView();\n        if (mapView != null) {\n            mapView.addReculateOverlayObserver(this);\n            mbAddReculateOverlayObserver = true;\n        }\n        ArrayList<LayerItem> items = new ArrayList<>();\n        for (int i = 0; i < pointList.size(); i++) {\n            /**< 根据点信息添加对应左飘图层 */\n            PointLayerItemDemo item = new PointLayerItemDemo("collision" + i, ItemDirection.ItemDirectionLeft);\n            if (item != null) {\n                String id = pointList.get(i).id;\n                if (id.isEmpty()) {\n                    id = String.valueOf(i);\n                }\n\n                item.setID(id + "left");\n                item.setBusinessType(DemoConstant.BusinessTypeCollisionPoint);\n                item.setPriority(i);\n                item.setPosition(pointList.get(i).mPos3D);\n\n                items.add(item);\n            }\n\n            /**< 根据点信息添加对应右飘图层 */\n            PointLayerItemDemo itemRight = new PointLayerItemDemo("collision" + i, ItemDirection.ItemDirectionRight);\n            if (itemRight != null) {\n                String id = pointList.get(i).id;\n                if (id.isEmpty()) {\n                    id = String.valueOf(i);\n                }\n\n                itemRight.setID(id + "right");\n                itemRight.setBusinessType(DemoConstant.BusinessTypeCollisionPoint);\n                itemRight.setPriority(i);\n                itemRight.setPosition(pointList.get(i).mPos3D);\n\n                items.add(itemRight);\n            }\n        }\n\n        addItems(items);\n    }\n}\n
\n    /**\n     * @brief                       图元样式JSON串回调接口\n     * @param[in] pLayer            图元所在图层\n     * @param[in] layerItem         需要更新样式的具体图元,通过GetBusinessType返回值判断具体图层\n     * @param[in] forJava           参数暂时无用,只是标示当前接口用于支持Java端,传递Stirng而非cJSON*\n     * @return String               返回的样式JSON内容字符串,由客户端构造\n     * @note thread:main\n     */\n    @Override\n    public String getLayerStyle(final BaseLayer pLayer, LayerItem layerItem, boolean forJava) {\n        String strStyleJson = "EMPTY";\n        if (pLayer == null || layerItem == null) {\n            return strStyleJson;\n        }\n\n        int itemType = layerItem.getItemType();\n        int businessType = layerItem.getBusinessType();\n\n        switch (itemType) {\n            case LayerItemType.LayerItemPointType:\n                switch (businessType) {\n                    case DemoConstant.BusinessTypeCollisionPoint:\n                        PointLayerItemDemo collisionItem = (PointLayerItemDemo)layerItem;\n                        if (collisionItem.mDirection == ItemDirection.ItemDirectionLeft) {\n                            strStyleJson = CommonUtil.getDemoStyleBeanJsonWithNightMode("collision_left_demo_style", mIsNightMode);\n                        } else {\n                            strStyleJson = CommonUtil.getDemoStyleBeanJsonWithNightMode("collision_right_demo_style", mIsNightMode);\n                        }\n                        break;\n                }\n                break;\n            default:\n                break;\n        }\n\n        return strStyleJson;\n    }\n

JSON配置

{\n  "demo_info": {\n\t\t"collision_left_demo_style": {\n\t\t\t"point_layer_item_style": {\n\t\t\t\t"normal_style": {\n\t\t\t\t\t"poi_marker_id": "global_image_guide_camera_surveillance_left_day",\n\t\t\t\t\t"poi_marker_info": "point_camera_left_x_y"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t"collision_right_demo_style": {\n\t\t\t"point_layer_item_style": {\n\t\t\t\t"normal_style": {\n\t\t\t\t\t"poi_marker_id": "global_image_guide_camera_surveillance_right_day",\n\t\t\t\t\t"poi_marker_info": "point_camera_right_x_y"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}
","body_lake":null,"pub_level":null,"status":"0","updated_at":"2022-04-06 07:04:32","deleted_at":null,"nameSpace":"mnlcaa/v610","browseCount":94,"collectCount":0,"estimateDate":23,"docStatus":0,"permissions":true,"overView":false}}