OpenLayers3 has some controls that provide functionality such as drawing features, modifying features, or selecting them. Our examples use three such controls in different combinations: ol.interaction.Draw, ol.interaction.Select, and ol.interaction.Modify. The names are self-explanatory, so let's see how the feature add' functionality is implemented:
onBtnAddToggle: function(btn, state){
this.clearMapInteractions();
if(!state){
return;
}
this.currentInteractions = {
draw: new ol.interaction.Draw({
type: 'Polygon',
source: this.vectorLayer.getSource()
})
};
this.currentInteractions.draw.on('drawend', this.onDrawEnd, this);
this.map.addInteraction(this.currentInteractions.draw);
}
The logic starts with a call to a function that takes care of disabling any controls that may have been active before. Then, if the Add feature button has been depressed, a Draw interaction is created and added to the map. A drawend event is wired up to the control so we can react whenever a feature has been added:
onDrawEnd: function(e){
this.saveFeature({
wkt: this.getWktFormat().writeGeometry(e.feature.getGeometry())
});
}
When a feature has been created, it is redirected to a proxy class that is responsible for handling the actual save procedure.
Editing a feature uses two controls - select interaction and modify interaction - so the user can first select a feature, and then edit it:
onBtnEditToggle: function(btn, state){
this.clearMapInteractions();
if(!state){
return;
}
var select = new ol.interaction.Select({
layers: [this.vectorLayer],
style: this.getEditSelectionStyle()
});
this.currentInteractions = {
select: select,
modify: new ol.interaction.Modify({
features: select.getFeatures(),
style: this.getEditStyle()
})
};
this.currentInteractions.select.on('select', this.onModifyStartEnd,
this);
this.map.addInteraction(this.currentInteractions.select);
this.map.addInteraction(this.currentInteractions.modify);
}
As you can see, the editing functionality setup is quite easy too. This time we instantiate select and modify interactions, but listen only to the select event. This is because it is assumed that an edit starts once a feature is selected and it ends once the user decides to deselect the feature:
onModifyStartEnd: function(e){
console.warn('[ol3] - modify start/end', e);
if(e.selected.length > 0){
//this is a select so just a start of edit
//simply store a wkt on a feature so can compare it later and
decide if an edit should happen
e.selected[0].tempWkt =
this.getWktFormat().writeGeometry(e.selected[0].getGeometry());
return;
}
var f = e.deselected[0],
modifiedWkt = this.getWktFormat().writeGeometry(f.getGeometry());
if(f.tempWkt === modifiedWkt){
console.warn('[ol3] - modify end - feature unchanged');
return;
}
console.warn('[ol3] - modify end - saving feature...');
this.saveFeature({
id: f.get('id'),
wkt: modifiedWkt
});
}
We can arrive in the select handler in two scenarios: when a user either selects or deselects a feature. On select, a snapshot of the feature geometry is taken so it can be compared with another snapshot taken when handling deselect. If the geometry has changed, the modified object is delegated to our proxy class for saving:
saveFeature: function(f){
console.warn('[CRUD PROXY] - saving...', f);
Ext.Ajax.request({
cors: true,
url: isNaN(f.id) ? apiEndPoint : (apiEndPoint + f.id),
method: isNaN(f.id) ? 'POST' : 'PUT',
success: Ext.bind(this.onSaveSuccess, this),
failure: Ext.bind(this.onSaveFailure, this),
params: f
});
}
Depending on ID presence, we send out either a POST request (creating a feature) or a PUT request (modifying a feature). For the sake of simplicity, once an operation succeeds, the map data is fully reloaded. This is far from being optimal, but lets us avoid syncing feature state.
The delete control is very similar to what we saw when editing, it doesn't use the modify interaction though. Whenever a user selects a feature, they are asked if they wish to remove a feature and if so, a DELETE request is made to the server. When finished, our feature set is re-read from the database.