arcgis api for js - 圖上標繪及測量
阿新 • • 發佈:2018-11-24
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"> <title>draw38_3.21</title> <!--此程式碼進行到,要實現繪圖介面。--> <!--<link rel="stylesheet" href="https://js.arcgis.com/3.21/dijit/themes/claro/claro.css">--> <!--<link rel="stylesheet" href="https://js.arcgis.com/3.21/esri/css/esri.css">--> <!--<script src="https://js.arcgis.com/3.21/"></script>--> <!--<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>--> <!--上下兩個介面都行。--> <link rel="stylesheet" href="https://js.arcgis.com/3.26/dijit/themes/nihilo/nihilo.css"> <link rel="stylesheet" href="https://js.arcgis.com/3.26/esri/css/esri.css"> <script src="https://js.arcgis.com/3.26/"></script> <style> html, body, #map { height: 100%; width: 100%; margin: 0; padding: 0; } #search { z-index: 20; height: 35px; width: 300px; position: absolute; top: 10px; left: 0; right: 0; margin: auto; } #drawOperations { z-index: 20; position: absolute; top: 60px; left: 5px; color: #444; width: 320px; overflow: auto; font-family: 仿宋體; border: solid 1px #4A92E7; border-radius: 4px; background-color: #fff; } /*#info button {*/ /*width: 30px;*/ /*height:30px;*/ /*line-height:30px;*/ /*text-align: center;*/ /*background-color: #fff;*/ /*padding: 2px;*/ /*margin: 4px;*/ /*cursor: pointer;*/ /*border-radius: 3px;*/ /*border: 1px solid*/ /*}*/ /*#info button:hover {*/ /*border: 1px solid #1e90ff;*/ /*}*/ #drawOperations img{ width:40px; margin:4px; } #drawOperations div{ padding:4px 4px 0px 4px; } </style> <!--匯入該js才可以新增多行文字。--> <script src="js/esri.symbol.MultiLineTextSymbol.js"></script> <script> require([ "esri/config", "esri/tasks/GeometryService", "esri/tasks/LengthsParameters", "esri/tasks/AreasAndLengthsParameters", "esri/symbols/TextSymbol", "esri/Color", "esri/symbols/Font", "esri/map", "esri/layers/ArcGISTiledMapServiceLayer", "esri/layers/ArcGISDynamicMapServiceLayer", "dojo/_base/connect", "esri/dijit/BasemapToggle", "esri/toolbars/draw", "esri/geometry/Point", "esri/geometry/Polyline", "esri/geometry/Polygon", "esri/dijit/Search", "dijit/registry", "esri/dijit/Scalebar", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/symbols/PictureMarkerSymbol", "esri/symbols/CartographicLineSymbol", "esri/symbols/PictureFillSymbol", "esri/geometry/Extent", "esri/SpatialReference", "esri/geometry/mathUtils", "esri/geometry/ScreenPoint", "esri/toolbars/edit", "dijit/Menu", "dijit/MenuItem", "dijit/MenuSeparator", "esri/graphic", "esri/layers/GraphicsLayer", "esri/dijit/Popup", "esri/dijit/PopupTemplate", "esri/InfoTemplate", "dojo/dom-construct", "dojo/dom", "dojo/on", "dojo/domReady!" ], function( esriConfig, GeometryService, LengthsParameters, AreasAndLengthsParameters, TextSymbol, Color, Font, Map, ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer, connect, BasemapToggle, Draw, Point, Polyline, Polygon, Search, registry, Scalebar, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, PictureMarkerSymbol, CartographicLineSymbol, PictureFillSymbol, Extent, SpatialReference, mathUtils, ScreenPoint, Edit, Menu, MenuItem, MenuSeparator, Graphic, GraphicsLayer, Popup, PopupTemplate, InfoTemplate, domConstruct,dom, on ) { //量算 function doMeasure(geometry) { let symbol; measureGeometry = geometry; draw.deactivate(); switch (geometry.type) { case "point": symbol = new SimpleMarkerSymbol(); break; case "polyline": symbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new dojo.Color([0, 0, 0]), 5); break; case "polygon": symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_NONE, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_DASHDOT, new dojo.Color([255, 0, 0]), 2), new dojo.Color([255, 255, 0, 0.25])); break; } //設定樣式 const graphic = new Graphic(geometry, symbol); map.infoWindow.hide(); graphicsLayer.add(graphic); //進行投影轉換,完成後呼叫projectComplete MeasureGeometry(geometry); } //投影轉換完成後呼叫方法 function MeasureGeometry(geometry) { // 如果為點型別,就測量經緯度。 if(geometry.type == "point"){ outputLongitudeLatitude(geometry.x, geometry.y); } // 如果為線型別就進行lengths距離測算 else if (geometry.type == "polyline") { const lengthParams = new LengthsParameters(); lengthParams.polylines = [geometry]; lengthParams.lengthUnit = GeometryService.UNIT_METER; lengthParams.geodesic = true; lengthParams.polylines[0].spatialReference = new SpatialReference(4326); geometryService.lengths(lengthParams); dojo.connect(geometryService, "onLengthsComplete", outputDistance); } // 如果為面型別,需要先進行simplify操作再進行面積測算 else if (geometry.type == "polygon") { const areasAndLengthParams = new AreasAndLengthsParameters(); areasAndLengthParams.lengthUnit = GeometryService.UNIT_METER; areasAndLengthParams.areaUnit = GeometryService.UNIT_SQUARE_METERS; this.outSR = new SpatialReference({wkid: 102113}); geometryService.project([geometry], this.outSR, function (geometry) { geometryService.simplify(geometry, function (simplifiedGeometries) { // 此處把各特徵點的經緯度,轉換成了座標系中的實際座標, // 單位即上邊規定的單位。 // console.log('simplifiedGeometries:', simplifiedGeometries); areasAndLengthParams.polygons = simplifiedGeometries; areasAndLengthParams.polygons[0].spatialReference = new SpatialReference(102113); geometryService.areasAndLengths(areasAndLengthParams); }); }); dojo.connect(geometryService, "onAreasAndLengthsComplete", outputAreaAndLength); } } // 顯示經緯度。 function outputLongitudeLatitude(lon, lat) { const curPos = new Point(lon, lat, map.spatialReference); let textSymbol = new TextSymbol( "經度:" + lon.toFixed(3) + "\n緯度:" + lat.toFixed(3) ).setColor(new Color([182,0,0])).setFont( new Font("14pt").setWeight(Font.WEIGHT_BOLD) ).setHorizontalAlignment("left").setVerticalAlignment('middle'); graphicsLayer.add(new Graphic(curPos, textSymbol)); } //顯示測量距離 function outputDistance(result) { const curX = measureGeometry.paths[0][measureGeometry.paths[0].length - 1][0]; const curY = measureGeometry.paths[0][measureGeometry.paths[0].length - 1][1]; const curPos = new Point(curX, curY, map.spatialReference); let len = parseInt(result.lengths[0]); let textSymbol; if(len <= 10000){ textSymbol = new TextSymbol( "長度:" + len + "米" ); } else{ textSymbol = new TextSymbol( "長度:" + len / 1000 + "千米" ); } let textSymbol2 = textSymbol.setColor(new Color([182,0,0])).setFont( new Font("14pt").setWeight(Font.WEIGHT_BOLD) ).setHorizontalAlignment("left").setVerticalAlignment('middle'); graphicsLayer.add(new Graphic(curPos, textSymbol2)); } // 顯示測量面積,周長。 function outputAreaAndLength(result) { const center = measureGeometry.getCentroid(); //獲取查詢區域的中心點 let area = parseInt(result.areas[0]); let len = parseInt(result.lengths[0]); let area2, len2; if(len > 10000){ len2 = len / 1000 } if(area > 1000000){ area2 = (area / 1000000).toFixed(3); } let textSymbol; if(area2 && len2){ textSymbol = new TextSymbol( "面積:" + area2 + "平方千米\n" + "周長:" + len2 + "千米" ); } else if(area2){ textSymbol = new TextSymbol( "面積:" + area2 + "平方千米\n" + "周長:" + len + "米" ); } else if(len2){ textSymbol = new TextSymbol( "面積:" + area + "平方米\n" + "周長:" + len2 + "千米" ); } else{ textSymbol = new TextSymbol( "面積:" + area + "平方米\n" + "周長:" + len + "米" ); } let textSymbol2 = textSymbol.setColor(new Color([182,0,0])).setFont( new Font("14pt").setWeight(Font.WEIGHT_BOLD) ).setHorizontalAlignment("left").setVerticalAlignment('middle'); graphicsLayer.add(new Graphic(center, textSymbol2)); } // 新增圖形 function addGraphic(event) { // 使繪圖工具無效。 // draw.deactivate(); // 啟用地圖導航. map.enableMapNavigation(); let symbol; if ( event.geometry.type === "point" || event.geometry.type === "multipoint") { symbol = markerSymbol; // symbol = textSymbol; } else if ( event.geometry.type === "line" || event.geometry.type === "polyline") { symbol = lineSymbol; } else { symbol = fillSymbol; } const measureOrNot = document.getElementById("measure").checked; // 繪製並測量 if(measureOrNot){ // 後邊的程式碼,不能處理extent型別的面積長度測量, // 所以此處需特殊處理。 // 即,把extent型別的資料結構,重構成polygon型別的資料結構,即可。 if(event.geometry.type == "extent"){ let center = event.geometry.getCenter(); // console.log('center:', center); const polygonJson = { "rings": [[ [event.geometry.xmin, event.geometry.ymax], [event.geometry.xmax, event.geometry.ymax], [event.geometry.xmax, event.geometry.ymin], [event.geometry.xmin, event.geometry.ymin], [event.geometry.xmin, event.geometry.ymax] ]], "spatialReference": {"wkid": 4326}, "cache": { "geoShape": "polygon", "_centroid": center, "_extent": { "xmin": event.geometry.xmin, "ymin": event.geometry.ymin, "xmax": event.geometry.xmax, "ymax": event.geometry.ymax, "spatialReference": event.geometry.spatialReference }, "_partwise": null, } }; const polygon = new Polygon(polygonJson); polygon.setCacheValue("geoShape", polygon.type); doMeasure(polygon); } else { event.geometry.setCacheValue("geoShape", event.geometry.type); doMeasure(event.geometry); } } // 只繪製不測量 else { event.geometry.setCacheValue("geoShape", event.geometry.type); graphicsLayer.add(new Graphic(event.geometry, symbol)); } } //初始化工具欄 function initToolbar() { // 建立繪圖工具。 // showTooltips,預設為true。 draw = new Draw(map, { showTooltips: true }); // 繪圖工具監聽畫完事件。 // console.log('graphicsLayer:', graphicsLayer); draw.on("draw-end", addGraphic); // undo_redo_arr,用於儲存 操作撤消恢復事件時 的圖形物件。 undo_redo_arr = []; // clear_arr,用於儲存清除操作,清除的所有圖形物件。 let clear_arr = []; let len_undo_redo, len_clear, len_graphics; let textAdd; // 繪圖工具監聽 繪製選項。 on(dom.byId("drawOptions"), "click", function(event){ // 沒有事件的元素。 if (event.target.id === "drawOptions" ) { return; } const tool = event.target.id.toLowerCase(); // 禁用地圖導航。 map.disableMapNavigation(); // 繪圖工具啟用相應的繪圖功能。 draw.activate(tool); if(textAdd){ textAdd.remove(); } }); // 監聽撤消事件。 on(dom.byId("undo"), "click", function(event) { // 每單擊一次按鈕,就更新一遍各個陣列的長度。 len_clear = clear_arr.length; len_graphics = graphicsLayer.graphics.length; // 撤消 清除事件。 if(len_graphics == 0 && len_clear != 0){ // 前提:檢視中沒圖形,清除陣列有圖形,才能撤消清除操作。 for(let i=0;i<len_clear;i++){ graphicsLayer.add(clear_arr.pop()); } } // 撤消單個圖形(多點繪製的是多個點,撤消的話一同撤消。) else if(len_graphics != 0){ // 前提:檢視中有圖形,才能撤消繪製的單個圖形。 let graphic_pop = graphicsLayer.graphics.pop(); // remove的兩個引數,第一個是要移除的圖形,第二個是該圖形對應的html元素節點。 graphicsLayer.remove(graphic_pop, graphic_pop.getNode()); undo_redo_arr.push(graphic_pop); // console.log('undo_redo_arr:', undo_redo_arr); } }); // 監聽恢復事件。 on(dom.byId("redo"), "click", function (event) { // 每單擊一次恢復按鈕,就更新一遍undo_redo_arr陣列的長度。 len_undo_redo = undo_redo_arr.length; // 恢復,是相對於撤消而言的。即,只能恢復撤消的圖形。 if(len_undo_redo != 0){ // 前提是 undo_redo_arr陣列不為空,才有圖形可恢復。 graphicsLayer.add(undo_redo_arr.pop()); } }); // 監聽清除事件。 on(dom.byId("clearAll"), "click", function (event) { // 清除檢視中所有圖形 事件。 len_graphics = graphicsLayer.graphics.length; for(let i=0;i<len_graphics;i++){ let graphic_pop = graphicsLayer.graphics.pop(); clear_arr.push(graphicsLayer.remove(graphic_pop, graphic_pop.getNode())); } }); // 監聽停止標繪事件。 on(dom.byId("stop"), "click", function (event) { draw.deactivate(); if(textAdd){ textAdd.remove(); } }); // 監聽新增文字事件。 on(dom.byId("text"), "click", function (event) { // console.log('text'); draw.deactivate(); textAdd = map.on("click", function(event) { // console.log('event:', event); graphicsLayer.add( new Graphic(event.mapPoint, new TextSymbol("Multi-Line \n Text"), {}) ); }); }); } //建立右鍵選單 function createGraphicsMenu(){ ctxMenuForGraphics = new Menu({}); // 在graphicsLayer圖層裡的元素,重疊時,也有上下之分。 // 即,重疊的元素,哪個在上邊,哪個在下邊。 //當滑鼠在graphicsLayer圖層的圖形上方時繫結該圖形的點選事件 let selected; graphicsLayer.on("mouse-over", function(event) { selected = event.graphic; // 右鍵選單繫結dom節點。不繫結則“刪除”標籤不彈出。 // event.graphic.getDojoShape().getNode(), 指的就是圖形對應的html方式的表達。 ctxMenuForGraphics.bindDomNode(event.graphic.getNode()); }); //當滑鼠移出graphicsLayer圖層的圖形上方時取消繫結該圖形的點選事件 graphicsLayer.on("mouse-out", function(event) { ctxMenuForGraphics.unBindDomNode(event.graphic.getNode()); }); ctxMenuForGraphics.addChild(new MenuItem({ label: "刪除", // 此處的onClick是在“刪除”標籤上的onClick。 onClick: function () { undo_redo_arr.push(graphicsLayer.remove(selected)); } })); } // 主函式。 function main() { // 建立popup彈出層 const popup = new Popup(null, domConstruct.create("div")); // 地圖 map = new Map("map", { slider:false, center: [121.47003707885744, 31.24853148977224], zoom: 7, infoWindow: popup, // 資訊視窗,彈窗。 extent: new Extent( -122.68,45.53,-122.45,45.60, new SpatialReference({wkid:4326 }) ) // 猜測:顯示的區域。 }); // 新增地圖圖層 const mapServiceURL = "https://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"; map.addLayer(new ArcGISTiledMapServiceLayer(mapServiceURL)); //初始化比例尺 const scalebar = new Scalebar({ map: map, attachTo: "bottom-left", scalebarUnit: "dual", }); //顯示比例尺 scalebar.show(); // 建立圖層 graphicsLayer = new GraphicsLayer({id: "draw"}); map.addLayer(graphicsLayer); // 給地圖新增事件。 map.on("load", initToolbar); // 建立右鍵選單。 createGraphicsMenu(); //啟動右鍵選單 ctxMenuForGraphics.startup(); //點選地圖響應 map.on("click", function(event) { //點選空白處隱藏popup if(event.graphic == undefined){ popup.hide(); } // mapPoint = event.mapPoint; }); // 搜尋框 const search = new Search({ map: map, graphicsLayer: graphicsLayer, }, "search"); search.startup(); // 彈出框資訊 graphicsLayer.on("click", function(event) { let details; if(event.graphic.geometry.getCacheValue("geoShape") == "polygon"){ // const details = '圖形: ' + event.graphic.geometry.cache.geoShape + '<br>'; details = '圖形型別: ' + event.graphic.geometry.getCacheValue("geoShape") + '<br>' + '圖形長度:' + event.graphic.geometry.getCacheValue("長度") + '<br>' + '圖形面積:' + event.graphic.geometry.getCacheValue("面積"); } else if(event.graphic.geometry.getCacheValue("geoShape") == "polyline"){ details = '圖形型別: ' + event.graphic.geometry.getCacheValue("geoShape") + '<br>' + '圖形長度:' + event.graphic.geometry.getCacheValue("長度"); } else if(event.graphic.geometry.getCacheValue("geoShape") == "point"){ details = '圖形型別: ' + event.graphic.geometry.getCacheValue("geoShape") + '<br>' + '經度:' + event.graphic.geometry.getCacheValue("經度") + '<br>' + '緯度:' + event.graphic.geometry.getCacheValue("緯度"); } popup.setTitle('圖形資訊'); // 彈窗標題 popup.setContent(details); // 彈窗內容 popup.show(event.mapPoint); // 彈窗展示及其位置 }); // textSymbol = new TextSymbol("Hello World").setColor( // new Color([128,0,0])).setAlign(Font.ALIGN_START).setAngle(45).setFont( // new Font("12pt").setWeight(Font.WEIGHT_BOLD)) ; //用來展示點的symbol //鏈式呼叫更有效能優勢。 markerSymbol = new SimpleMarkerSymbol().setSize(10).setColor( new Color("#00FFFF")).setStyle(SimpleMarkerSymbol.STYLE_SQUARE); // 用來展示線的symbol lineSymbol = new CartographicLineSymbol( CartographicLineSymbol.STYLE_SOLID, // 線的樣式,實線。 new Color([255, 0, 0]), 4, // 顏色 線寬 CartographicLineSymbol.CAP_ROUND, // 兩頭圓角 CartographicLineSymbol.JOIN_MITER, // 連線處尖角 5 // 最大斜接長度。斜接長度指的是在兩條線交匯處內角和外角之間的距離。 // 如果斜接長度超過 miterLimit 的值,邊角會以 lineJoin 的 "bevel" 型別來顯示。 ); //用來展示面的symbol fillSymbol = new SimpleFillSymbol( SimpleFillSymbol.STYLE_SOLID, // 實心 new SimpleLineSymbol( SimpleLineSymbol.STYLE_SOLID, // 實線 new Color('#fff'), // 邊/線的顏色 1 // 邊的寬度 ), new Color([0, 255, 0, 0.2]) // 填充的顏色及透明度 ); // console.log('markerSymbol:', markerSymbol); // console.log('lineSymbol:', lineSymbol); // console.log('fillSymbol:', fillSymbol); } let map, graphicsLayer, draw, ctxMenuForGraphics; let measureGeometry, undo_redo_arr; let textSymbol, markerSymbol,lineSymbol,fillSymbol; const geometryServiceUrl="http://10.254.11.41:6080/arcgis/rest/services/Utilities/Geometry/GeometryServer"; esriConfig.defaults.io.proxyUrl = "/proxy/"; esriConfig.defaults.io.alwaysUseProxy = false; const geometryService = new GeometryService(geometryServiceUrl); // esriConfig.defaults.geometryService = new GeometryService(geometryServiceUrl); main(); }); </script> </head> <body class="claro" style="font-size: 0.75em;"> <!--繪圖選單。--> <div id="drawOperations"> <div align="left">繪圖選項</div> <div id="drawOptions"> <img id="Point" src="../images/multipoint.png"> <img id="Line" src="../images/line.png"> <img id="Polyline" src="../images/polyline.png"> <img id="FreehandPolyline" src="../images/freehand_polyline.png"> <img id="Triangle" src="../images/triangel.png"> <img id="Extent" src="../images/rectangle.png"> <img id="Circle" src="../images/circle.png"> <img id="Ellipse" src="../images/ellipse.png"> <img id="Polygon" src="../images/polygon.png"> <img id="FreehandPolygon" src="../images/freehand_ploygon.png"> </div> <div>新增文字</div> <img id="text" src="../images/text.png"> <div><input id="measure" type="checkbox">繪製並測量</div> <button id="stop">停止標繪</button> <button id="undo">撤消</button> <button id="redo">恢復</button> <button id="clearAll" >清除</button> </div> <!--搜尋框。--> <div id="search"></div> <!--地圖顯示區。--> <div id="map" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'"> </div> </body> </html>