1.
Introduction
In this document I will explain how basic
editing can be done through the ArcGis JavaScript API. With the Silverlight API, basic
editing is much sampler than in Javascript. The edit tool in Silverlight offers
much more methods than does the editor in the JavaScript API. So in the
JavaScript implementation I do not use the editor tool dijit but instead I use the edit
toolbar. The only dijit used in the editing is the template picker, which is
used for creating features. Simple feature creation is done by the draw toolbar component.
In the coming 10.1 version you will be able to use
versioning when doing web editing. I hope later to add support for this new
important feature.
Basic feature editing consists of the
following commands:
·
Select features for a feature
class
·
Clear all selected features for
a feature class
·
Select the feature layer for
editing
·
Create new features
·
Delete selected features
·
Edit attribute data for a
feature
·
Save update or creation of a
feature
·
Undo changes
·
Create features from a symbol
template
Below is the schema how the JavaScript application
framework is build. The basic idea is separation of the GUI from GIS commands and
tools and the ArcGis Javascript API. The
main webpage has almost no Javascript code embedded. Only some initialization
code for the dojo library can be found on the page.
To keep the GIS framework library
independent from the application, there is no interaction between the view
model and the ArcGis Javascript API. knockoutJS
plays an important role for the exchange of data between the web page and the view
models.
Before you can do editing on feature
classes, a proxy URL must be installed to handle a POST that exceed the 2048
character size limit. Lucky, ESRI provide us the full source code for the
implementation of the proxy URL that replace the POST request with a GET when the maximum size is reached. The GET request has no
size limitations. You find all the information for implementing the proxy URL
at http://help.arcgis.com/en/webapi/javascript/arcgis/help/jshelp_start.htm
.The toolbar used for doing the basic editing consists of a dropdown list,9 buttons and a checkbox used for displaying a template of symbols. All this elements interact with the view model of the web page.
2.
Selections
Before editing can be done, an editable
layer must be selected. This can be done through a combo box that is built with
the feature layers. The data source of the combo box is an observable
collection from knockoutJS. By using an observable collection the selection is
automatically reflected in the view model. The implementation is illustrated
below :
HTML :
<select data-bind="options:
editLayers, optionsText: 'layerName', value: selectedLayer, optionsCaption:
'Choose edit layer'" style="vertical-align:top"></select>
View model :
this.editLayers = ko.observableArray();
this.selectedLayer = ko.observable();
The array editLayers contains the title and
the layer id of the feature layer. Using the layer id we set the layer
information in the GisEditing class. In setting the layer information, I also add at the
same time the selection symbols so that after drawing the result it is always
visible even if the feature is not saved in the database yet.
setEditLayer: function
(featureLayerName) {
var
featureLayer = GisOperation.getMap().getLayer(featureLayerName);
if
(featureLayer != null) {
currentFeatureLayer =
featureLayer;
if
(currentFeatureLayer.geometryType == "esriGeometryPolygon")
{
currentFeatureLayer.setSelectionSymbol(gisOperation.getSelectionFillSymbol());
currentDrawGeometryType =
esri.toolbars.Draw.POLYGON;
currentSymbol =
gisOperation.getSelectionFillSymbol();
}
else
if (currentFeatureLayer.geometryType == "esriGeometryPolyline") {
currentFeatureLayer.setSelectionSymbol(gisOperation.getSelectionLineSymbol());
currentDrawGeometryType =
esri.toolbars.Draw.POLYLINE;
currentSymbol =
gisOperation.getSelectionLineSymbol();
}
else
if (currentFeatureLayer.geometryType == "esriGeometryPoint") {
currentFeatureLayer.setSelectionSymbol(gisOperation.getSelectionMarkerSymbol());
currentDrawGeometryType =
esri.toolbars.Draw.POINT;
currentSymbol =
gisOperation.getSelectionMarkerSymbol();
}
return
true;
}
else
return
false;
},
Now everything is ready for doing the
creation and editing of features.
3.
Create feature
As of all command and tools, the processing
starts at the main view model using two exposed methods : execute and
canexecute using the same technique used in the viewmodel of Silverlight.
this.addExecute = function () {
editToolbarGeneralViewModel.addCommand(setEditActive, this.selectedLayer().layerName);
};
this.addCanExecute =
ko.computed(function () {
return
!this.isEditActive();
}, this);
The canexecute is used to enable or disable
commands. The reason for adding this extra method is to block some command
during the processing of commands and tools. When you press create a feature,
you expect that only the save or undo button is available, other buttons like
edit a feature or delete a feature must be disabled. The use of knockoutJS will
help us to enable or disable this without a lot of coding.
First I create an observable isEditActive that will reflect if some Create / Update /
Delete is active. By setting this observable to a new value all properties
CanExecute get also a new value.
<input data-bind="click:
selectExecute, enable: selectCanExecute" class="buttonTool" id="btnSelect" type="image" src="Images/SelectionSelectTool32.png" />
<input data-bind="click:
clearSelectExecute, enable: clearSelectCanExecute" class="buttonTool"
id="btnClearSelect"
type="image"
src="Images/SelectionClearSelection32.png" />
<input data-bind="click:
deleteExecute, enable: deleteCanExecute" class="buttonTool" id="btnDelete" type="image" src="Images/EditingFixErrorTool32.png" />
<input data-bind="click:
addExecute, enable: addCanExecute" class="buttonTool" id="btnAdd" type="image" src="Images/EditingPolygonTool32.png" />
<input data-bind="click:
editExecute, enable: editCanExecute" class="buttonTool" id="btnEdit" type="image" src="Images/EditingEditShape32.png" />
<input data-bind="click:
movePointExecute, enable: movePointCanExecute" class="buttonTool"
id="btnMovePoint"
type="image"
src="Images/TinEditingTinNodeMove32.png" />
<input data-bind="click:
attributeExecute, enable: attributeCanExecute" class="buttonTool"
id="btnAttribute"
type="image"
src="Images/EditingCreateFeaturesWindowShow32.png"/>
<input data-bind="click:
saveExecute, enable: saveCanExecute" class="buttonTool" id="btnSave" type="image" src="Images/EditingSaveEdits32.png"/>
<input data-bind="click:
undoExecute, enable: undoCanExecute" class="buttonTool" id="btnUndo" type="image" src="Images/EditUndo32.png"/>
Instead of using jQuery to do the dom
processing for enable / disable buttons, a much simpler solution is the use of
the view model implementation of knockoutJS.
In Javascript I used the draw toolbar
component for doing the drawing of new features. In Silverlight I could use the
editor for doing the creation of features. As I explained earlier, the edit
dijit has only limited methods exposed, so making it not suitable for custom
edit toolbars. Still creating a feature is only a couple of lines.
startCreateOperation: function (editOperationComplete) {
if (currentFeatureLayer == null)
return;
try {
currentOperation = "CreateFeature";
currentGeometry = null;
drawCreateEndHandlerHandle =
dojo.connect(gisOperation.getDraw(), "onDrawEnd",
drawCreateEndHandler);
gisOperation.getDraw().activate(currentDrawGeometryType);
} catch (err) {
logMessage("E",
"startCreateOperation failed -->"
+ err.message, "gisEditing");
}
},
After creation of the geometry, a callback
is defined to display the result. Because we don’t do directly save, the
geometry is first put on the graphics layer. Only during a save the actual feature
is displayed on the map.
// Eventhandler for the end of a geometry
drawn
function drawCreateEndHandler(geometry)
{
try {
currentGeometry = geometry;
//
Drawn graphic with the geometry drawn
if
(currentOperation == "CreateFeature")
{
var
newGraphic = new esri.Graphic(currentGeometry,
currentSymbol, currentAttributes);
gisOperation.getMap().graphics.add(newGraphic);
gisOperation.getMap().graphics.refresh();
//
start attribute editing
}
if
(drawCreateEndHandlerHandle != null)
dojo.disconnect(drawCreateEndHandlerHandle);
} catch
(err) {
logMessage("E",
"drawCreateEndHandler failed -->"
+ err.message, "gisEditing");
}
}
Because this result is only a geometry, the
feature contains null values for all its attributes. I will add later attribute
editing during the creation of geometries. This will only result in the calling
of an attribute edit.
4.
Edit of the geometry of a
feature
The command consist of the combination of
two actions. First, select a feature with the select button. Next press the
edit vertices button to activate the display of vertices.
The editing of vertices makes use of the
edit toolbar component. Initialization is done during the init of the
GisEditing component.
editTool = new esri.toolbars.Edit(gisOperation.getMap());
To start editing of vertices, activate the edit toolbar with the correct
parameters for the selected feature.
startEditOperation: function (editOperationComplete) {
if
(currentFeatureLayer == null)
return;
try
{
if
(currentFeatureLayer.getSelectedFeatures().length == 1) {
currentOperation = "EditVertices";
currentAttributes =
currentFeatureLayer.getSelectedFeatures()[0].attributes;
editTool.activate(esri.toolbars.Edit.MOVE | esri.toolbars.Edit.EDIT_VERTICES,
currentFeatureLayer.getSelectedFeatures()[0]);
}
} catch
(err) {
logMessage("E", "startEditOperation
failed -->" + err.message, "gisEditing");
}
},
Ending of the edit is done through a save
or undo command. This version only handle single edit operations.
5.
Creation of features
As part of the basic editing functionality,
we support two ways to create features :
·
Simple create new feature
·
Create feature based on a
template of feature types
The use of templates is only a front end
for a simpler creation of features with some attribute values already filled in.
This functionality relies entirely on the simple feature creation.
The creation of feature is not build upon
the editor dijit but rather on a simple draw action using the draw toolbar. At
the end of the draw operation a new feature is build based on the geometry and
on the contents of initialized attributes. In the case of a simple feature
creation, the attribute table is empty, for use of template, attributes are
prefilled with values coming from the exposed templates of the feature service.
startCreateOperation: function (editOperationComplete) {
if (currentFeatureLayer == null)
return;
try {
currentOperation = "CreateFeature";
currentGeometry = null;
drawCreateEndHandlerHandle =
dojo.connect(gisOperation.getDraw(), "onDrawEnd",
drawCreateEndHandler);
gisOperation.getDraw().activate(currentDrawGeometryType);
} catch
(err) {
logMessage("E",
"startCreateOperation failed -->"
+ err.message, "gisEditing");
}
},
As you can see, the creation is based on
the draw action. At the end of the draw action, control is passed to a callback
method. This method does not do a lot of processing, simple save the geometry
for later use in the save and display the result on the graphic layer so that
you can see what has been drawn.
// Eventhandler for the end of a geometry
drawn
function drawCreateEndHandler(geometry) {
try {
currentGeometry = geometry;
//
Drawn graphic with the geometry drawn
if
(currentOperation == "CreateFeature")
{
var
newGraphic = new esri.Graphic(currentGeometry,
currentSymbol, currentAttributes);
gisOperation.getMap().graphics.add(newGraphic);
gisOperation.getMap().graphics.refresh();
//
start attribute editing
}
if
(drawCreateEndHandlerHandle != null)
dojo.disconnect(drawCreateEndHandlerHandle);
} catch
(err) {
logMessage("E",
"drawCreateEndHandler failed -->"
+ err.message, "gisEditing");
}
}
I will later add support for attribute
editing at the end of the creation of the feature. As you can see now this will be
done in the callback method.
Use of templates
Creation of feature with the help of a
template of feature types is a special way of creating features. The main
difference is that by using feature types, you can use attribute values that
has been prefilled in and exposed by the feature service.
To simplify the job, I use the template picker
dijit from the ArcGis JavaScript API.
This dijit can be used independent of the editor dijit and makes the creation
of a template having all the different feature types much easier.
Because the template picker is a visual
object, I have to create a div that will serve as container for this object. The
creation of the template is done in the view model of the edit toolbar. In
principle this violates the MVVM rules, but because we don’t have the same
possibilities as in Silverlight this was the easiest way to do. You could try
to build it yourself with a knockoutJS template, but displaying symbols will
result in much more code to write.
By using this ESRI dijit, we still need to
do some extra work to handle the selection on the template.
·
We must keep track of the
attribute values that are related to the feature type.
·
Start the create feature on the
feature class of the feature type
This is not so difficult to do as you can
see in the code below
initializeTemplatePicker: function (templateDiv, setEditActive) {
try {
setEditActiveEdit =
setEditActive;
viewModel.visibleTemplates(true);
var
contentHolder = "<div id='" +
templateDiv + "'></div>";
require(["dojo/dom-construct"], function (domConstruct) {
domConstruct.place(contentHolder, "editTemplates", "first");
});
templatePicker = new esri.dijit.editing.TemplatePicker({
featureLayers:
GisOperation.getFeatureLayers(),
rows: "auto",
columns: "auto",
showTooltip: true,
style: "height: 100%; width: 250px;"
}, templateDiv);
templatePicker.startup();
dojo.connect(templatePicker, "onSelectionChange", function () {
var
selected = templatePicker.getSelected();
GisEditing.initAttributes(selected.template.prototype.attributes);
editToolbarGeneralViewModel.addCommand(setEditActive,
selected.featureLayer.name);
});
} catch (err) {
logMessage("E", "error
in initializeTemplatePicker ->" + err.name + "\n" + err.message, "editToolbarGeneralVM");
}
},
The template dijit is created in code and
added to the DOM and not in the HTML code because of problems with the destroy
method of the ESRI dijit component. By creating the dijit in code you can dynamically
add and delete the template in code. All the prefilled attributes can be found
in the template prototype property.
The checkbox will create the edit templates
by adding the template picker to the corresponding window. Simply clicking on
the icon will start the create functionality.
Another possibility is creating a custom
template from scratch by using the items object in the constructor. This will
however include more coding at the selection level. In our general approach for
implementing an webmap editor, using the template picker based on edit layers is the
more straightl solution.
Putting ESRI objects by code into an accordion
container can be tricky. Elements of the container are only available after it
has been selected. So you must use the selectChild event to create the template
picker.
var accResults =
dijit.byId("leftAccordion");
dojo.connect(accResults, "selectChild", function
(childPane) {
if (childPane.id == "editTemplates" &&
viewModel.useTemplates()) {
if
(!editToolbarGeneralViewModel.isTemplatePickerInitialized()) {
editToolbarGeneralViewModel.initializeTemplatePicker(TemplatePickerDiv,
setEditActive);
}
}
});
In a next document I will explain in detail how the save or undo commands work.
Wow…Thats impressive such a informative blog thanks for sharing it.
ReplyDeletetoolbar editor