openlayers入门开发系列之地图标绘篇

本篇的重点内容是利用openlayers实现地图标绘功能,效果图如下:

实现思路:

  • 标绘界面设计以及标绘面板控制 
<!-- 标绘面板 -->
<div style="position:absolute;right:180px;top:80px;display:none;" id="map_toolbar_plot">
<ul class="map_toolbar_list_more map_toolbar_list_more-float  clearfix">
       <li plottype="freehandline">自由线<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar28"></span></li>
       <li plottype="polyline">折线<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar29"></span></li>
       <li plottype="arc">弧线<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar30"></span></li>
       <li plottype="curve">曲线<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar31"></span></li>
       <li plottype="freehandpolygon">手绘<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar32"></span></li>
       <li plottype="polygon">多边形<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar33"></span></li>
       <li plottype="rectangle">矩形<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar34"></span></li>
       <li plottype="circle">圆形<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar35"></span></li>
       <li plottype="assaultdirection">直角<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar36"></span></li>
       <li plottype="squadcombat">简单<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar37"></span></li>
       <li plottype="tailedsquadcombat">燕尾<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar38"></span></li>
       <li plottype="gatheringplace">集结地<span class="map_toolbar_list_more_icon map_toolbar_list_more_icon-toolbar39"></span></li>
</ul>
</div>
//标绘例
var plotFlag = false;
$("#bPlot").click(function () {
            if(!plotFlag){
                $("#map_toolbar_plot").show();
                plotFlag = true;
            }else{
                bmap.setCurrentMutexInteraction(null);
                if(bmap.dataClear){
                    bmap.dataClear.clear();
                }
                $("#map_toolbar_plot").hide();
                plotFlag = false;            
            }            
});
  • 标绘初始化以及核心代码实现
//标绘
var plottypes = ["freehandline","polyline","arc","curve","freehandpolygon","polygon",
        "rectangle","circle","assaultdirection","squadcombat","tailedsquadcombat","gatheringplace"];
for(var i = 0; i < plottypes.length; i++){
        var selector = '#map_toolbar_plot li[plottype="' + plottypes[i] + '"]';
        $(selector).on('click',function(){
            var id = "10006";//标绘工具
            var tool = bmap.getIndexInteraction(id);
            bmap.setCurrentMutexInteraction(tool);
            //标绘类型
            var plottype = $(this).attr("plottype");
            tool.setDrawType(plottype);
        })
}
/**
 *  获取带有索引的交互工具
 * @param index {String}
 * @returns {ol.interaction.Interaction|null}
 */
bxmap.Map.prototype.getIndexInteraction = function(index){
    var indexInteractions = this.get("bxmap_index_interactions") || {};
    var interaction = indexInteractions[index];
    if(interaction == null && this.getMap()){
        var arr = this.getMap().getInteractions().getArray();
        for(var i = 0; i < arr.length ; i++){
            if(index == arr[i].get(bxmap.INDEX_INTERACTION_ID)){
                interaction = arr[i];
                indexInteractions[index] = interaction;
                break;
            }
        }
    }
    return interaction;
}
/*----------标绘,依赖于GISpace动态标绘开源项目{bxmap.interaction.Plot}---------*/
/**
 * @classdesc 用于绘制军事图形,依赖于依赖于GISpace动态标绘开源项目
 * @extends {ol.interaction.Interaction}
 * @param options {Object}
 * @param options.type {String} 默认为polygon
 */
bxmap.interaction.Plot = function (options) {
    ol.interaction.Interaction.call(this, { handleEvent: this._handleKeyboardEvent });

    this.plotDraw = null; //绘制标绘
    this.plotEdit = null; //编辑标绘
    this.plotLayer = null; //标绘图层
    this.editingFeatures = new ol.Collection();//当前选择的要素

    var opt_options = options ? options : {};
    var type = opt_options.type ? opt_options.type : "polygon";
    this.setDrawType(type);
}
ol.inherits(bxmap.interaction.Plot, ol.interaction.Interaction);

/**
 * @inheritdoc
 * @param {ol.CanvasMap} map
 */
bxmap.interaction.Plot.prototype.setMap = function (map) {
    ol.interaction.Interaction.prototype.setMap.call(this, map);
    if (map) {
        // 初始化标绘绘制工具,添加绘制结束事件响应
        var plotDraw = this.plotDraw = new P.PlotDraw(map);
        plotDraw.on("draw_end", this._onDrawEnd, this);
        // 初始化标绘编辑工具
        var plotEdit = this.plotEdit = new P.PlotEdit(map);

        var plotLayer = this.plotLayer = this.getLayer();
        // 添加标绘图层
        map.addLayer(plotLayer);

        map.on('click', function (e) {
            if (plotDraw.isDrawing()) return;

            var feature = map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
                return feature;
            },{
                layerFilter: function (layer) {
                    return layer == plotLayer;
                },
                hitTolerance: 1 //1像素,仅在canvas render中有效
            });
            if (feature) {
                this.editingFeatures.push(feature);
                // 开始编辑
                this._startEditingFeature(feature);
            } else {
                // 结束编辑
                this._finishEditing();
            }
        },this);
    }
}

/**
 * @description 设置绘制图形类型
 * @param {String} drawType 类型
 * @example <caption> 图形类型有效值 </caption>
 *     arc - 弧线,curve - 曲线,polyline - 折线,freehandline - 自由线,circle - 圆,
 *     ellipse - 椭圆,closedcurve - 曲线面,polygon - 多边形,rectangle - 矩形,
 *    freehandpolygon - 自由面,gatheringplace - 聚集地,doublearrow - 钳击双箭头,
 *  straightarrow - 直线箭头,finearrow - 细直箭头,assaultdirection - 直箭头,
 *    attackarrow - 进攻箭头,tailedattackarrow - 燕尾进攻箭头,squadcombat - 斜箭头,
 *     tailedsquadcombat - 燕尾斜箭头
 */
bxmap.interaction.Plot.prototype.setDrawType = function(drawType){
    var map = this.getMap();
    this.setCursor("default");
    
    this.set("draw_type", drawType);
    if (this.plotEdit && this.plotDraw) {
        this._finishEditing();
        this.plotDraw.activate(drawType);
    }
}

/**
 * @description 获取绘制图形类型
 * @returns {String} 绘制图形类型
 */
bxmap.interaction.Plot.prototype.getDrawType = function(){
    return this.get("draw_type");
}

/**
 * @private
 * @description 更新状态
 */
bxmap.interaction.Plot.prototype._updateState = function() {
    var map = this.getMap();
    if(map == null) return;
    var active = this.getActive();
    
    var plotDraw = this.plotDraw;
    var plotEdit = this.plotEdit;
    var layer = this.plotLayer;
    //使激活
    if(active){
        this._finishEditing();
        if(plotDraw){
            var type = this.getDrawType();
            plotDraw.activate(type);
        }
    }else{
        if(plotDraw){
            plotDraw.deactivate();
        }
        this._finishEditing();
    }
};

/**
 * @description 获取标绘图层
 * @returns {ol.layer.Vector}
 */
bxmap.interaction.Plot.prototype.getLayer = function(){
    if(this.plotLayer == null){
        // 设置标绘符号显示的默认样式
        var stroke = new ol.style.Stroke({color: '#FF0000', width: 2});
        var fill = new ol.style.Fill({color: 'rgba(0,255,0,0.4)'});
        var image = new ol.style.Circle({fill: fill, stroke: stroke, radius: 8});
        drawStyle = new ol.style.Style({image: image, fill:fill, stroke:stroke});
    
        // 绘制好的标绘符号,添加到FeatureOverlay显示。
        var layer = this.plotLayer = new ol.layer.Vector({
            source: new ol.source.Vector()
        });
        layer.setStyle(drawStyle);
        layer.setZIndex(bxmap.CONFIG_LEVEL_MAX + 1);//保持不被其他图层覆盖
    }
    return this.plotLayer;
}

/**
 * @private
 * @param mapBrowserEvent
 * @returns {Boolean}
 */
bxmap.interaction.Plot.prototype._handleKeyboardEvent = function(mapBrowserEvent) {
    var stopEvent = false;
    var map = this.getMap();
    if(map == null) return;
    
    if (mapBrowserEvent.type == "keydown" || mapBrowserEvent.type == "keypress") {
        var keyEvent = mapBrowserEvent.originalEvent;
        var keyCode = keyEvent.keyCode;
        if (keyCode == 46) {//delete
            var plotLayer = this.plotLayer;
              this.editingFeatures.forEach(function (elem, index, array) {
                  var close = elem.get("close_button");
                  if (close) {
                      map.removeOverlay(close);
                  }
                  var source = plotLayer.getSource();
                source.removeFeature(elem);
              });
              this.plotEdit.deactivate();
              this.editingFeatures.clear();    
              mapBrowserEvent.preventDefault();
              stopEvent = true;
      }
    }
    return !stopEvent;
}

/**
 * @private
 * @description 绘制结束后,添加图层显示。
 */
bxmap.interaction.Plot.prototype._onDrawEnd = function (event) {
    var feature = event.feature;
    if (feature) {
        //添加到编辑要素集合中
        this.editingFeatures.push(feature);
        //添加到标绘图层
        this.plotLayer.getSource().addFeature(feature);
        this._bindingCloseButton(feature);
        //开始编辑
        this._startEditingFeature(feature);
    }
}

/**
 * @private
 * @description 开始编辑要素
 */
bxmap.interaction.Plot.prototype._startEditingFeature = function (feature) {
    if (this.plotEdit == null || feature == null) return;
    this.plotEdit.activate(feature);
    var close = feature.get("close_button");
    var map = this.getMap();
    if (map && close) {
        var exists = map.getOverlayById(close.getId());
        if(exists == null){
            map.addOverlay(close);
        }
        var ctlPoints = this.plotEdit.getControlPoints();
        var point = ctlPoints[ctlPoints.length - 1];
        close.setPosition(point);
    }
}

/**
 * @private
 * @description 结束编辑要素
 */
bxmap.interaction.Plot.prototype._finishEditing = function () {
    if (this.plotEdit) {
        this.plotEdit.deactivate();
    }

    //移除删除按钮
    var map = this.getMap();
    if (map == null) return;
    this.editingFeatures.forEach(function (elem, index, array) {
        var close = elem.get("close_button");
        if (map && close) {
            map.removeOverlay(close);
        }
    });
    this.editingFeatures.clear();
}

/**
 * @private
 * @description 获取删除按钮
 * @returns {ol.Overlay}
 */
bxmap.interaction.Plot.prototype._bindingCloseButton = function (feature) {
    if (feature == null) return;
    var uid = bxmap.common.getCounterUid(feature);
    var closeTooltipElement = document.createElement('div');
    closeTooltipElement.className = 'tooltip tooltip-close';
    var closeOverlay = new ol.Overlay({
        id: uid,
        element: closeTooltipElement,
        offset: [16, 0],
        positioning: 'bottom-center'
    });
    closeTooltipElement.innerHTML = '<a style="color:red;" href:"#" title="删除">✘</a>';
    feature.set("close_button", closeOverlay);

    //执行删除绘制
    var plotLayer = this.plotLayer;
    var plotEdit = this.plotEdit;
    var map = this.getMap();
    closeTooltipElement.addEventListener('click', function () {
        if (plotEdit && feature && plotLayer) {
            var close = feature.get("close_button");
            if (close && map) {
                map.removeOverlay(close);
            }
            var source = plotLayer.getSource();
            source.removeFeature(feature);
            plotEdit.deactivate();
        }
    });
}

/**
 * 清空绘制的数据
 */
bxmap.interaction.Plot.prototype.clear = function(){
    this._finishEditing();
    if(this.plotLayer){
        this.plotLayer.getSource().clear();
    }
}

 

<think>我们正在讨论前端开发中的WebGIS开发。根据用户提供的引用和之前的对话历史,我们可以总结出WebGIS开发在前端中的关键点。用户之前已经了解了GIS开发的定义和内容,现在专注于前端部分。 引用内容回顾: [^1]: 提到了WebGIS开发入门,涉及BIM+GIS,即将BIM模型展现在WebGIS地图的真实位置上,使用倾斜摄影、obj、3dtiles等格式。 [^2]: 关于WebGIS开发面试题(前端一),讨论了多页面应用之间的通信,使用浏览器存储(localStorage, Cookie)或服务器方式(如websocket)。 [^3]: 关于WebGIS开发面试题(前端三),比较了Vuex和Pinia两种状态管理库。 [^4]: WebGIS——Cesium小白前端开发历程,介绍了使用React框架和Cesium库进行WebGIS开发的基本步骤,包括初始化球、加载图层等。 根据以上引用和用户的问题,我们组织回答如下: 回答结构: 1. 定义WebGIS前端开发 2. 核心技术栈(包括地图引擎、框架、状态管理) 3. 核心功能实现(地图初始化、图层加载、交互等) 4. 数据通信与存储(涉及多页面通信、本地存储等) 5. 总结与学习建议 注意:在回答中适当引用提供的材料,并自然标注引用标记。</think>### WebGIS前端开发详解 WebGIS前端开发是GIS开发的核心分支,专注于**地理空间数据的可视化、交互式地图构建及用户界面设计**,通过浏览器实现地理信息的动态展示与分析。以下是其核心内容与技术要点: --- #### 一、核心技术栈 1. **地图引擎框架** - **Leaflet/OpenLayers**:轻量级二维地图库,支持图层叠加、标记制、事件交互[^1]。 - **Cesium**:三维地球引擎,支持实景三维模型(如倾斜摄影、BIM模型)、地形高程加载、相机飞行控制[^4]。 - **天地图API**:集成国产地图服务,提供底图、标注等基础功能[^4]。 2. **前端框架与状态管理** - **React/Vue**:构建组件化地图应用,如通过`react-cesium`封装三维场景[^4]。 - **状态管理**: - **Vuex/Pinia**:管理地图状态(如图层显隐、相机参数),Pinia支持模块化且兼容TypeScript[^3]。 - **浏览器存储**:用`localStorage`或`sessionStorage`缓存地图数据,实现多页面状态同步[^2]。 3. **空间数据格式** - **3D Tiles**:Cesium专属格式,支持海量三维模型(如BIM、实景三维)的流式加载[^1][^4]。 - **GeoJSON**:轻量级矢量数据标准,用于制点、线、面要素。 - **倾斜摄影数据**(如OSGB):通过Cesium转换为3D Tiles后展示真实场景[^1]。 --- #### 二、核心功能实现 1. **地图初始化与基础操作** ```javascript // Cesium初始化示例[^4] const viewer = new Cesium.Viewer('mapContainer', { baseLayer: Cesium.ImageryLayer.fromProviderAsync( Cesium.TianDiTuImageryProvider({ style: 'img', // 天地图影像底图 }) ), terrain: Cesium.Terrain.fromWorldTerrain() // 加载全球地形 }); viewer.scene.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(116.4, 39.9, 1000) }); // 飞行至北京 ``` 2. **三维模型加载与交互** - BIM模型集成:将Revit等格式转换为3D Tiles,叠加到真实坐标位置[^1]。 - 图层控制:动态切换模型显隐,实现多源数据融合。 ```javascript const bimTileset = viewer.scene.primitives.add( new Cesium.Cesium3DTileset({ url: 'models/bim/tileset.json' }) ); // 隐藏BIM图层 bimTileset.show = false; ``` 3. **多页面通信与数据持久化** - 使用`localStorage`同步地图状态(如视角坐标、图层配置)[^2]: ```javascript // 保存相机参数 localStorage.setItem('cameraPosition', JSON.stringify(viewer.camera.position)); // 跨页面读取 const savedPos = JSON.parse(localStorage.getItem('cameraPosition')); viewer.camera.setView({ destination: savedPos }); ``` - **WebSocket**:实时更新动态数据(如交通流量、传感器监测)[^2]。 --- #### 三、典型应用场景 1. **智慧城市** - 实景三维城市模型:融合倾斜摄影与BIM,实现建筑剖切、日照分析[^1][^4]。 - 基础设施管理:地下管线、道路网络的立体展示。 2. **环境监测** - 热力图展示污染分布,结合GeoJSON划定保护区边界。 3. **应急指挥** - 实时标绘灾害点位,规划救援路径(路径规划需调用后端空间分析服务)。 --- #### 四、学习路径建议 1. **入门**:掌握Leaflet/OpenLayers基础API,实现标记、弹窗等交互[^1]。 2. **进阶**: - 学习Cesium的三维场景构建(地形、模型加载)[^4]。 - 集成状态管理(如Pinia+Vue)管理复杂地图状态[^3]。 3. **实战**: - 将BIM/倾斜摄影数据转换为3D Tiles并加载[^1]。 - 优化大数据量渲染(如分页加载、细节层次LOD)。 > 提示:WebGIS与传统前端开发的核心差异在于**空间数据处理能力**(如坐标转换、投影变换)和**高性能渲染优化**(如WebGL应用)[^1][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值