In this document I will treat the creation
of more advanced editing tools. In the ArcGis JavaScript API it is difficult to
perform geometric operations on geometry in code. However to perform advanced geometry
operations we can use the geometry services available at an ArcGis server.
Using this technique you are able to perform actions as split and merge of
geometries. By combining geometry
service methods you can even more complex operations. Some tools will using the
basic edit commands as building blocks for creation of advanced tools is. In another example I will show how you can use the JavaScript API
to build tools in native JavaScript code without geometry services.
Another solution for extending editing by
using Service Object Extensions (SOE). They can play the same role as does the
geometry services of the ArcGis Server.2. Primary geometry tools
Two important tools in web editing are the
split and merge tool. Like other commands explained in previous documents, the
tools are triggered through the use of the MVVM (Model View ViewModel+) library and buttons on the web page.
2.1 Split tool
The split tool for a polygon was
implemented by doing the following steps:
·
Select a feature (polygon)· Draw a polygon that is used for the split action
· Perform the method cut of the geometry service
· Update the feature with one part of the split
· Add the remaining parts as new features to the layer, attribute information is copied from the
selected feature.
The split command in the ViewModel looks contains the following code:
splitCommand: function () {try {
// Verify a polygon is selected on the layer specified
var featureLayer = null;
if (commonData.ViewModel.selectedLayer() != null)
featureLayer = gisOperation.getMap().getLayer(
commonData.ViewModel.selectdLayer().layerName);
if (featureLayer == null
|| featureLayer.geometryType != "esriGeometryPolygon"
|| featureLayer.getSelectedFeatures().length < 1) {
// Invalid choice
commonData.ViewModel.setEditActive(false);
return;
}
// Draw line
gisCommon.drawGeometry(esri.toolbars.Draw.POLYLINE, splitLineComplete);
} catch (err) {
logging.logMessage("E", "error in splitCommand ->" + err.name + " - "
+ err.message, "editToolbarAdvancedVM");
}
}
After
the line has been drawn, in the callback the cut operation is executed through
a call to the gisEditing class. A callback method is added in case some past processing is needed at the
business level. All tools contains also an error processing method, very
important there are errors during the geometry service method calls.
function splitLineComplete(polyline) {
// Do now second part , doing the split
if (polyline) {
var featureLayer =
gisOperation.getMap().getLayer(commonData.ViewModel.selectedLayer().layerName);
var selectGraphics = featureLayer.getSelectedFeatures();
gisEditing.splitOperation(featureLayer, selectGraphics, polyline,
splitComplete, geometryServiceFailed);
} else {
return;
}
} The real job is done in the editing framework I implemented through the use of the cut method of the rest service.
splitOperation: function (featureLayer, selectGraphics, polyline,
geometryServiceComplete, geometryServiceFailed) {
try {
if (geometryServiceFailed == null)
geometryServiceFailed = defaultErrorHdl;
var selectGeometries = new Array();
for (var i = 0; i < selectGraphics.length; i++) {
selectGeometries.push(selectGraphics[i].geometry);
}
currentCallBack = geometryServiceComplete;
currentFeatureLayer = featureLayer;
gisOperation.getGeometryService().cut(selectGeometries, polyline,
splitCompleted, geometryServiceFailed);
} catch (err) {
logging.logMessage("E", "split failed ->" + err.message,
"gisEditing/splitOperation");
}
} In the edit framework I do also care about the replication of the attribute information and update and add the features. Therefor the callback of the cut is treated within the edit class, and after the replication and feature update is done, the callback is executed of the calling command.
function splitCompleted(results) {
try {
if (results.geometries != null && results.geometries.length > 1) {
// Update first geometry
var updateGraphic =
currentFeatureLayer.getSelectedFeatures()[0].setGeometry(results.geometries[0]);
// Create for the next
currentSymbol = currentFeatureLayer.getSelectedFeatures()[0].symbol;
var newGraphics = new Array();
for (var i = 1; i < results.geometries.length; i++) {
newGraphics.push(new esri.Graphic(results.geometries[i],
currentSymbol,currentAttributes));
}
currentFeatureLayer.applyEdits(newGraphics, [updateGraphic], null,
function (addResults, updateResults, deleteResults) {
var updateCount = updateResults.length + addResults.length;
if (currentCallBack != null)
currentCallBack(results.geometries);
}, function (err) {
logging.logMessage("E", "applyEdits failed for first feature -->"
+ err.message, "gisEditing/splitCompleted");
if (currentCallBack != null)
currentCallBack(null);
});
}
} catch (err) {
logging.logMessage("E", "splitcomplete failed failed -->"
+ err.message, "gisEditing/splitCompleted");
}
}
The
procedure outlined in this split tool is used for all the advanced tools I
implemented. In general what I do is
1. Do the necessary selection of
features.2. Activate the tool , in general done through a button click.
3. If needed, start a drawing action through the general draw tool.
4. Use a callback to step to a next action.
5. Repeat step 4 if different actions are required.
This is
in general the steps you will do if you
would add a tool to ArcMap as an extension. In development of extension you
will use events instead of callback methods.
It is
also very important to encapsulate all code into a try catch structure. During
writing tools often the asynchronous working of geometry operations can results
in unexpected behaviors.2.2 Merge tool
The
merge tool can be considered as a command in terms of ArcMap. This tool is much
simpler than the split tool. For the merge I only require the selection of a
number of features of a feature layer. As is the case with the split tool, the
geometry service of an ArcGis server will do the hard work of doing a union of
the geometries.
The only
asynchronous process required after the merge is the post processing of the
features on the feature layer, consisting of an update of a feature (geometry)
and a delete of a feature.The startup of the merge tool is similar as that with the split tool:
unionGeometries: function (featureLayer, selectGraphics, geometryServiceComplete, geometryServiceFailed) {
try {
if (geometryServiceFailed == null)
geometryServiceFailed = defaultErrorHdl;
currentCallBack = geometryServiceComplete;
currentFeatureLayer = featureLayer;
var selectGeometries = new Array();
for (var i = 0; i < selectGraphics.length; i++) {
selectGeometries.push(selectGraphics[i].geometry);
}
gisOperation.getGeometryService().union(selectGeometries,
unionGeometriesCompleted, geometryServiceFailed);
} catch (err) {
logging.logMessage("E", "merge polygons failed -->"
+ err.message, "gisEditing/unionGeometries");
}
} As you can see, a lot of code is similar to the code used for the split tool. The most important line is the call to the geometry service ‘union’. The callback function will gives us the merged geometry. The only thing that remains is the update and delete of features so that only one feature remains as the result of the merge.
3.
Native JavaScript tools
It is not always an easy job to create
geometry tools entirely in JavaScript. However the ArcGis JavaScript API offers
some possibilities through the composition of the polygon and polyline classes.
One example of a tool from ArcMap that can be reproduced in JavaScript is the
explode tool. The principle is very
simple :· For polygons, isolate each ring into a separate geometry (polygon)
· For polylines, Isolate each path into a separate geometry (polyline)
You only need to delete the original
feature and replace it by a number of new features corresponding to the number
of rings or paths. No round trip is required to the ArcGis server for creating
the different geometries, only an asynchronous operation is required to update
the feature class with the new features.
explode: function (featureLayer, feature, callback) {
try {
var updateFeatures = new Array();
var addFeatures = new Array();
var deleteFeatures = new Array();
var actionRequired = false;
var newGeometry;
var newGraphic;
currentAttributes = clone(feature.attributes);
currentAttributes[featureLayer.objectIdField] = null;
currentSymbol = feature.symbol;
currentFeatureLayer.clearSelection();
switch (feature.geometry.type) {
case "polygon":
// Create number of features of rings in the geometry
if (feature.geometry.rings.length > 1) {
actionRequired = true;
var ringCount = feature.geometry.rings.length;
for (var i = 0; i < ringCount; i++) {
newGeometry =
new esri.geometry.Polygon(feature.geometry.spatialReference);
newGeometry.addRing(feature.geometry.rings[i]);
newGraphic =
new esri.Graphic(newGeometry, currentSymbol, currentAttributes);
addFeatures.push(newGraphic);
}
deleteFeatures.push(feature);
}
break;
case "polyline":
// Create number of features of paths in the geometry
if (feature.geometry.paths.length > 1) {
for (var i = 0; i < feature.geometry.paths.length; i++) {
newGeometry =
new esri.geometry.Polyline(feature.geometry.spatialReference);
newGeometry.addPath(feature.geometry.paths[i]);
newGraphic =
new esri.Graphic(newGeometry, currentSymbol, currentAttributes);
addFeatures.push(newGraphic);
}
deleteFeatures.push(feature);
actionRequired = true;
}
break;
default:
break;
}
if (actionRequired) {
featureLayer.applyEdits(addFeatures, updateFeatures, deleteFeatures,
function (addResults, updateResults, deleteResults) {
var updateCount = updateResults.length + addResults.length;
callbackExplode(updateCount, callback);
}, function (err) {
logging.logMessage("E", "explode failed -->" +
err.message, "gisEditing/explode");
});
}
else {
callbackExplode(0, callback);
}
} catch (err) {
logging.logMessage("E", "explode failed -->" +
err.message, "gisEditing/explode");
}
}
As you can see not very complex to realize.
This tool will work in polygons and polylines that consist of simple multipart
geometries. If the geometry consist of a complex structure, this tool could
fail, in the current implementation failures are logged.
Another example of a tool that can be
realized in JavaScript is the lengthening or shortening of a polyline over a
given distance ,at the start or at the end of the polyline. This is a tool that
I previous developed as an ArcMap tool. Here you can use methods exposed in the
esri.geometry namespace.
4.
Complex geometry tools
A first tool is the creation of a polygon based
on points added on the map. This tools involves two buttons, one to start
creation of points on the map. A second button will create the polygon using
the method convexHul l of the geometry service of an ArcGis server. Even as the tool looks complex, the
implementation is very simple, thanks to the geometry service of an ArcGis
server.
createConvexHull: function (featureLayer, pointList, geometryServiceComplete,geometryServiceFailed) {
try {
gisOperation.disableDrawMode();
if (geometryServiceFailed == null)
geometryServiceFailed = defaultErrorHdl;
currentCallBack = geometryServiceComplete;
currentFeatureLayer = featureLayer;
var points = new Array();
for (var i = 0; i < pointList.length; i++) {
points.push(pointList[i].geometry);
}
gisOperation.getGeometryService().convexHull(points, convexHullCompleted,
geometryServiceFailed);
} catch (err) {
logging.logMessage("E", "ConvexHull failed -->"
+ err.message, "gisEditing/createConvexHull");
}
}
In the callback we can now add the
resulting geometry as new feature to the feature class selected using default
values for the attributes.
5.
Conclusion
In this first part I illustrated how easy
you can add custom tools to an editing ArcGis application. Doing some advanced
editing requires the use of a geometry service at an ArcGis Server. When you
required some functionality not found in the geometry service, you can move
towards the creation of Server Object Extensions(SOE) and use them in the same way as using geometry
services. Knowledge of ArcObjects is a must when starting with writing Server
Object Extensions. Use always rest
services when using SOE’s, you don’t want
XML from a SOAP web service, but Json. Adding SOE’s is best done in a
separate Dojo class, so it can only be loaded when needed through AMD.
Thanks for the great article. is it possible to cut a polygon with an another polygon instead of a line.
ReplyDeleteyou need to put your code in action like a demo or something. people will only read your long letter if they think the demo is something they are looking for. jesus christ.
ReplyDeleteHi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me..
ReplyDeleteI am a regular follower of your blog. Really very informative post you shared here.
Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Training in Chennai .
or Javascript Training in Chennai.
Nowadays JavaScript has tons of job opportunities on various vertical industry. ES6 Training in Chennai
I found Hubwit as a transparent s ite, a social hub which is a conglomerate of Buyers and Sellers who are ready to offer online digital consultancy at decent cost. editing rejected articles
ReplyDeleteIt is perfect time to make some plans for the future and it is time to be happy. I've read this post and if I could I desire to suggest you some interesting things or suggestions. Perhaps you could write next articles referring to this article. I want to read more things about it! editing rejected manuscripts
ReplyDelete