Legacy: This topic discusses extending operation views created using the Windows version Operations Dashboard for ArcGIS. This version of the application has been moved to Mature Support status. To learn more about the latest web-based version, see Operations Dashboard for ArcGIS.
A map tool in an operation view allows users to interact with a map widget. For example, the measure tool allows users to find the length or area of a shape that they define on a map widget. In this tutorial, you'll develop, style, and test a buffer map tool. The map tool calculates and displays buffer rings around a point drawn on the map.
View tutorial filesBefore you begin, you must set up your development environment for Windows Operations Dashboard and prepare it to run in developer mode.
In the extensions folder that you created in the Set up a development environment tutorial, create a folder called bufferMapTool, and create the following files in that folder:
Note: You can assign different names to the above files.
Open the buffer.json file, or the manifest file, in your IDE and add the following text:
{ "type": "Map Tool", "title": "Buffer Map Tool", "description": "A map tool that creates buffers from a clicked point", "usePortalServices": true, "runtime": { "iconPath": "icon.png", "path": "buffer.html", "viewType": "toolbar" }, "credits": "By Esri (http://www.esri.com)" }
Note:
Note: For this map tool to work, this property must be set to true.
To learn more about the manifest file properties, see Manifest file.
<!DOCTYPE html> <html lang="en"> <head> <meta charset=utf-8"> <link rel="stylesheet" type="text/css" href="style.css"> </head>
<body>
</body> </html>
<body>
tags, create a <div>
and add a data-dojo-type
attribute with the value extension/BufferMapTool. This attribute is used to instantiate an
extension/BufferMapTool object that comes from the buffer map tool dijit you'll create in the
BufferMapTool.js file.
<body>
<div data-dojo-type="extension/BufferMapTool"></div>
</body>
<div class="table">
<div class="table-cell labelContainer">
Click on the map to create three buffers for the clicked location.
</div>
<div class="table-cell buttonContainer">
<button data-dojo-attach-event="onClick:deactivateMapTool">Done</button>
</div>
</div>
<script>
block before the closing
<body>
tag. The script block contains the dojoConfig object that will load the buffer tool
dijit. To learn more about dojoConfig, see Dojo Toolkit documentation.
<script> var dojoConfig = { async: true, paths: { "extension": location.pathname.replace(/\/[^/]+$/, '') } }; </script>
<div>
element in this
file.
<script>
block from the previous step, add another <script>
block
referencing the ArcGIS API for JavaScript. You must load the dojoConfig before loading the ArcGIS API for
JavaScript, otherwise the dojoConfig will be ignored.
<script src="//js.arcgis.com/3.26/"></script>
<script>
block, add the Dojo parser:
<script> require(["dojo/parser","dojo/domReady!"] , function(parser){ parser.parse(); }); </script>
define
function. This will be used to define the buffer
map tool dijit. This function loads the dependent modules and passes the modules as parameters in a callback function.
define([ "dojo/_base/declare", "dojo/_base/lang", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "esri/opsdashboard/MapToolProxy", "esri/tasks/BufferParameters", "esri/tasks/GeometryService", "esri/Color", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/symbols/PictureMarkerSymbol", "esri/graphic", "dojo/text!./bufferMapToolTemplate.html" ], function (declare, lang, _WidgetBase, _TemplatedMixin, MapToolProxy, BufferParameters, GeometryService, Color, SimpleLineSymbol, SimpleFillSymbol, PictureMarkerSymbol, Graphic, templateString ) { });
... "dojo/text!extension/bufferMapToolTemplate.html" ], function (declare, lang, _WidgetBase, _TemplatedMixin, MapToolProxy, BufferParameters, GeometryService, Color, SimpleLineSymbol, SimpleFillSymbol, PictureMarkerSymbol, Graphic, templateString ) { return declare("BufferMapTool", [_WidgetBase, _TemplatedMixin, MapToolProxy], { }); });
return declare("BufferMapTool", [_WidgetBase, _TemplatedMixin, MapToolProxy], { templateString: templateString, });
constructor: function () { // The buffer parameters this.bufferParams = new BufferParameters(); this.bufferParams.unit = GeometryService.UNIT_METER; this.bufferParams.distances = [500, 1000, 2000]; // Create the graphic for the push pin var iconPath = location.href.replace(/\/[^/]+$/, '/'); var symbol = new PictureMarkerSymbol(iconPath + "pushpin.png", 15, 30); symbol.yoffset = 10; this.pushPinGraphic = new Graphic(null, symbol); // Create the buffer graphics var outlineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color("#000000"), 1); var bufferSymbol = new SimpleFillSymbol(SimpleLineSymbol.STYLE_SOLID, outlineSymbol, null); this.bufferGraphics = []; for (var i = 0; i < 3; i++) { this.bufferGraphics.push(new Graphic(null, bufferSymbol)); } },
hostReady: function () { //Set up the UI of the map tool and create the graphics layer //when the host (Operations Dashboard) is ready // Retrieve the geometry service specified for the organization // Note: The buffer.json manifest file must have the "usePortalServices" set to true // in order for the geometry service (and any other helper services) to be retrieved if (!this.portalHelperServices || !this.portalHelperServices.geometry) { alert("Cannot get the geometry service required for creating buffers."); this.deactivateMapTool(); return; } // Update the buffer params with the target map widget spatial reference this.bufferParams.outSpatialReference = this.mapWidgetProxy.spatialReference; // Setup a geometry service this.geometryService = new GeometryService(this.portalHelperServices.geometry.url); // Update the size of the user experience this.setDisplaySize({ width: Math.min(this.availableDisplaySize.width / 2, 400), height: 40 }); // Creates two graphics layers to control the order of draw buffers below the pushpin. return this.mapWidgetProxy.createGraphicsLayerProxy().then(lang.hitch(this, function (graphicsLayerProxy) { this.bufferGraphicsLayerProxy = graphicsLayerProxy; return this.mapWidgetProxy.createGraphicsLayerProxy().then(lang.hitch(this, function (graphicsLayerProxy) { this.pushPinGraphicsLayerProxy = graphicsLayerProxy; // Activate the drawing activity when the graphics layer is ready this.activateMapDrawing({geometryType: "point"}); })); })); },
availableDisplaySizeChanged: function (availableSize) { // Update the size of the user experience this.setDisplaySize({ width: Math.min(availableSize.width / 2, 400), height: 40 }); },
mapDrawComplete: function (geometry) { // When the drawing activity has been performed by the user, use the resulting geometry // to calculate the buffer rings and display them on the map if (!geometry) return; // Clear the graphics layer. this.bufferGraphicsLayerProxy.clear(); this.pushPinGraphicsLayerProxy.clear(); // Immediately show a feedback for the user this.showPushPin(geometry); // Starts the buffering process this.showBuffers(geometry); },
showPushPin: function (geometry) { // Update the position of the push pin graphic this.pushPinGraphic.setGeometry(geometry); // Update the host graphics layer this.pushPinGraphicsLayerProxy.addOrUpdateGraphic(this.pushPinGraphic); },
showBuffers: function (geometry) { // Use the geometry service to calculate 3 buffer rings around the clicked point // Update the buffer params this.bufferParams.geometries = [geometry]; // When the buffer rings have been calculated, call this.onBufferResult to update the graphics this.geometryService.buffer(this.bufferParams, lang.hitch(this, function (geometries) { if (!geometries || geometries.length === 0) return; // For each of the buffer geometries, update the buffer graphics for (var i = 0; i < geometries.length; i++) { this.bufferGraphics[i].setGeometry(geometries[i]); } // Update the host graphics layer this.bufferGraphicsLayerProxy.addOrUpdateGraphics(this.bufferGraphics); })); },
deactivateMapTool: function () { // Deactivate the map tool when the Done button is clicked // Clean up then deactivating this.deactivateMapDrawing(); this.mapWidgetProxy.destroyGraphicsLayerProxy(this.bufferGraphicsLayerProxy); this.mapWidgetProxy.destroyGraphicsLayerProxy(this.pushPinGraphicsLayerProxy); // Call the base function this.inherited(arguments, []); }
define([ "dojo/_base/declare", "dojo/_base/lang", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "esri/opsdashboard/MapToolProxy", "esri/tasks/BufferParameters", "esri/tasks/GeometryService", "esri/Color", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/symbols/PictureMarkerSymbol", "esri/graphic", "dojo/text!./bufferMapToolTemplate.html" ], function (declare, lang, _WidgetBase, _TemplatedMixin, MapToolProxy, BufferParameters, GeometryService, Color, SimpleLineSymbol, SimpleFillSymbol, PictureMark-erSymbol, Graphic, templateString) { return declare("BufferMapTool", [_WidgetBase, _TemplatedMixin, MapToolProxy], { templateString: templateString, constructor: function () { // The buffer parameters this.bufferParams = new BufferParameters(); this.bufferParams.unit = GeometryService.UNIT_METER; this.bufferParams.distances = [500, 1000, 2000]; // Create the graphic for the push pin var iconPath = location.href.replace(/\/[^/]+$/, '/'); var symbol = new PictureMarkerSymbol(iconPath + "pushpin.png", 15, 30); symbol.yoffset = 10; this.pushPinGraphic = new Graphic(null, symbol); // Create the buffer graphics var outlineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color("#000000"), 1); var bufferSymbol = new SimpleFillSymbol(SimpleLineSymbol.STYLE_SOLID, outlineSymbol, null); this.bufferGraphics = []; for (var i = 0; i < 3; i++) { this.bufferGraphics.push(new Graphic(null, bufferSymbol)); } }, hostReady: function () { //Set up the UI of the map tool and create the graphics layer //when the host (Operations Dashboard) is ready // Retrieve the geometry service specified for the organization // Note: The buffer.json manifest file must have the "usePortalServices" set to true // in order for the geometry service (and any other helper services) to be retrieved if (!this.portalHelperServices || !this.portalHelperServices.geometry) { alert("Cannot get the geometry service required for creating buffers."); this.deactivateMapTool(); return; } // Update the buffer params with the target map widget spatial reference this.bufferParams.outSpatialReference = this.mapWidgetProxy.spatialReference; // Set up a geometry service this.geometryService = new GeometryService(this.portalHelperServices.geometry.url); // Update the size of the user experience this.setDisplaySize({ width: Math.min(this.availableDisplaySize.width / 2, 400), height: 40 }); // Creates two graphics layers to control the order of draw buffers below the push-pin. return this.mapWidgetProxy.createGraphicsLayerProxy().then(lang.hitch(this, function (graphicsLayerProxy) { this.bufferGraphicsLayerProxy = graphicsLayerProxy; return this.mapWidgetProxy.createGraphicsLayerProxy().then(lang.hitch(this, function (graphicsLayerProxy) { this.pushPinGraphicsLayerProxy = graphicsLayerProxy; // Activate the drawing activity when the graphics layer is ready this.activateMapDrawing({geometryType: "point"}); })); })); }, availableDisplaySizeChanged: function (availableSize) { // Update the size of the user experience this.setDisplaySize({ width: Math.min(availableSize.width / 2, 400), height: 40 }); }, mapDrawComplete: function (geometry) { // When the drawing activity have been performed by the user, use the resulting geometry // to calculate the buffer rings and display them on the map if (!geometry) return; // Clear the graphics layer. this.bufferGraphicsLayerProxy.clear(); this.pushPinGraphicsLayerProxy.clear(); // Immediately show a feedback for the user this.showPushPin(geometry); // Starts the buffering process this.showBuffers(geometry); }, showPushPin: function (geometry) { // Update the position of the push pin graphic this.pushPinGraphic.setGeometry(geometry); // Update the host graphics layer this.pushPinGraphicsLayerProxy.addOrUpdateGraphic(this.pushPinGraphic); }, showBuffers: function (geometry) { // Use the geometry service to calculate 3 buffer rings around the clicked point // Update the buffer params this.bufferParams.geometries = [geometry]; // When the buffer rings have been calculated, call this.onBufferResult to update the graphics this.geometryService.buffer(this.bufferParams, lang.hitch(this, function (geometries) { if (!geometries || geometries.length === 0) return; // For each of the buffer geometries, update the buffer graphics for (var i = 0; i < geometries.length; i++) { this.bufferGraphics[i].setGeometry(geometries[i]); } // Update the host graphics layer this.bufferGraphicsLayerProxy.addOrUpdateGraphics(this.bufferGraphics); })); }, deactivateMapTool: function () { // Deactivate the map tool when the Done button is clicked // Clean up then deactivating this.deactivateMapDrawing(); this.mapWidgetProxy.destroyGraphicsLayerProxy(this.bufferGraphicsLayerProxy); this.mapWidgetProxy.destroyGraphicsLayerProxy(this.pushPinGraphicsLayerProxy); // Call the base function this.inherited(arguments, []); } }); });
With the map tool's UI and logic in place, add the following text to the style.css file to customize the look and feel of the extension:
html, body { height: 100%; width: 100%; margin: 0; padding: 0; overflow: hidden; } .table { display: table; width: 100%; height: 100%; } .table-cell { display: table-cell; vertical-align: middle; padding: 5px; } .labelContainer { font-family: Arial; font-size: 12px; width: 100%; } .buttonContainer { width: 20px; }
Your extension is now ready for testing in Operations Dashboard.
Note: To learn more about creating an operation view, see Create an operation view in the PDF of the operation view help.
The map tool is now included in the map toolbar.
If you need to debug your extension, you can use the Operations Dashboard extension debugger.
Tip: You can also click Dev Mode in the upper right corner of the app, and click Open Debug Tool to launch the debugger.
Note: The URL displayed at the top of the debugger window starts from localhost:<port number> because the extension is running from a local server.
Tip: If you make a change to the HTML or JavaScript files during debugging, you can click the Refresh button at the upper left corner of the debugger to see the update. However, if you make any changes to the manifest, you will need to close and reopen the operation view.
To debug your extension using the web browser, complete the following steps:
The operation view will open in your default browser.
Note: Since you're hosting your extension in the local server, you need to keep the Windows app and the local server running.