/*---------------- FILE HEADER --------------------------------------- This file is part of Geoide. Copyright (C) 2005-2006 by: IDgis B.V. http://www.idgis.nl This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Herman Assink IDgis bv P.O. Box 15 7450 AA Holten The Netherlands E-Mail: herman.assink@idgis.nl * @version 1.4.0 * @author IDgis team * ------------------------------------------------------------------------*/ import nl.idgis.giclient.geoma.Envelope; import nl.idgis.giclient.geoma.Geometry; import nl.idgis.giclient.geoma.GMLFactory; import nl.idgis.giclient.geoma.NullGeometry; import nl.idgis.giclient.gis.FeatureEventDispatcher; import nl.idgis.giclient.gis.GILayer; import nl.idgis.giclient.util.SynchListener; import nl.idgis.giclient.io.XMLResponse; import nl.idgis.giclient.io.XMLResponseListener; import nl.idgis.giclient.io.XMLServer; import nl.idgis.giclient.util.XMLTools; import nl.idgis.giclient.webserviceconnector.wfsconnector.FeatureType; import nl.idgis.giclient.event.StateEventDispatcher; import nl.idgis.giclient.event.StateEventListener; import nl.idgis.giclient.event.StateEvent; import nl.idgis.giclient.event.ChangeEvent; import nl.idgis.giclient.geoma.GeometryListener; import nl.idgis.giclient.gis.SelectableLayer; class nl.idgis.giclient.gis.Feature implements GeometryListener, XMLResponseListener { static var ADDED:Number = 0; static var UNCHANGED:Number = 1; static var CHANGED:Number = 2; static var REMOVED:Number = 3; static var WAITING:Number = 4; private var id:String = ""; private var layer:GILayer = null; private var featureType:FeatureType = null; private var persistent:Boolean = false; private var envelope:Envelope = null; private var geometry:Geometry = null; private var columnValues:Object = null; private var editingStatus:Number = -1; private var waiting:Boolean = false; private var idVerified:Boolean = false; private var envelopeLoading:Boolean = false; private var envelopeSynchListeners:Array = null; private var requestingProperties:Boolean = false; private var iconUrl:String = null; private var editable:Boolean = true; private var annotation:String = null; var featureEventDispatcher:FeatureEventDispatcher = null; var stateEventDispatcher:StateEventDispatcher = null; function Feature(id:String, layer:GILayer, featureType:FeatureType, persistent:Boolean, idVerified:Boolean) { this.id = id; this.layer = layer; this.featureType = featureType; this.persistent = persistent; this.idVerified = idVerified; columnValues = new Object(); annotation = ""; featureEventDispatcher = new FeatureEventDispatcher(); stateEventDispatcher = new StateEventDispatcher(); } function setIconUrl(iconUrl:String) { this.iconUrl = iconUrl; stateEventDispatcher.dispatchEvent(new StateEvent(this, "Feature", StateEvent.CHANGE, "iconUrl")); } function getIconUrl():String { return this.iconUrl; } function onChangeGeometry(geometry:Geometry, permanent:Boolean):Void { if (permanent) { pushUndo(geometry); } else { change(); } } function getID():String { return id; } function getLayer():GILayer { return layer; } function getFeatureType():FeatureType { return featureType; } function setEnvelope(envelope:Envelope):Void { this.envelope = envelope; } function getEnvelope():Envelope { if (geometry == null) { return envelope; } else { return geometry.getEnvelope(); } } function setGeometry(geometry:Geometry):Void { if (this.geometry == geometry) { return; } if (this.geometry != null) { this.geometry.geometryEventDispatcher.removeGeometryListener(this); change(); } this.geometry = geometry; if (geometry == null) { envelope = null; } else { if (geometry instanceof NullGeometry) { envelope = new NullGeometry(); } else { envelope = geometry.getEnvelope(); } geometry.geometryEventDispatcher.addGeometryListener(this); } featureEventDispatcher.changeFeatureGeometry(this); _root["apiInterpreter"].onChangeGeometry(geometry, true); // TODO GEOMETRY EVENT MODEL } function getGeometry():Geometry { return geometry; } function setColumnValue(columnName:String, columnValue:String):Void { doSetColumnValue(columnName, columnValue, false); } function doSetColumnValue(columnName:String, columnValue:String, noEvent:Boolean):Void { var sLayer:SelectableLayer = SelectableLayer(getLayer()); if(sLayer != null) { if(columnName == sLayer.getAnnotationColumn()) { this.annotation = columnValue; } } columnValues[columnName] = columnValue; if (!noEvent) { change(); } } function getColumnValue(columnName:String):String { return columnValues[columnName]; } function isPersistent():Boolean { return persistent; } private function change():Void { if (editingStatus == UNCHANGED) { editingStatus = CHANGED; //stateEventDispatcher.dispatchEvent(new ChangeEvent(this, "Feature", "editingStatus", UNCHANGED)); } stateEventDispatcher.dispatchEvent(new ChangeEvent(this, "Feature", "editingStatus", UNCHANGED)); } function remove():Void { if ((editingStatus == UNCHANGED) || (editingStatus == CHANGED)) { var previousEditingStatus:Number = editingStatus; editingStatus = REMOVED; stateEventDispatcher.dispatchEvent(new ChangeEvent(this, "Feature", "editingStatus", previousEditingStatus)); } } function getEditingStatus():Number { if (waiting) { return WAITING; } else { return editingStatus; } } function setEditing(editing:Boolean):Void { if (isEditing() == editing) { return; } var previousEditingStatus:Number = getEditingStatus(); if (editing) { if (persistent) { editingStatus = UNCHANGED; } else { editingStatus = ADDED; } } else { editingStatus = -1; } stateEventDispatcher.dispatchEvent(new ChangeEvent(this, "Feature", "editingStatus", previousEditingStatus)); } function isEditing():Boolean { if (editingStatus > -1) { return true; } return false; } function setWaiting(waiting:Boolean):Void { if (this.waiting == waiting) { return; } var previousEditingStatus:Number = getEditingStatus(); this.waiting = waiting; stateEventDispatcher.dispatchEvent(new ChangeEvent(this, "Feature", "editingStatus", previousEditingStatus)); } function isIDVerified():Boolean { return idVerified; } function loadEnvelope(synchListener:SynchListener):Void { var serverRequest:String = "request=GetFeature"; serverRequest += "&layer=" + layer.getName(); serverRequest += "&id=" + id; serverRequest += "&srs=" + getLayer().getSRS(); var serverParams:Object = new Object(); serverParams["discriminator"] = "ENVELOPE"; serverParams["synchListener"] = synchListener; var xmlServer:XMLServer = new XMLServer(layer.getServerURL()); xmlServer.load(this, serverRequest, serverParams); } function requestProperties(synchListener:SynchListener):Void { if (requestingProperties) { return; } var serverRequest:String = "request=GetFeature"; serverRequest += "&layer=" + layer.getName(); serverRequest += "&id=" + id; serverRequest += "&srs=" + getLayer().getSRS(); var serverParams:Object = new Object(); serverParams["discriminator"] = "PROPERTIES"; serverParams["synchListener"] = synchListener; requestingProperties = true; var xmlServer:XMLServer = new XMLServer(layer.getServerURL()); xmlServer.load(this, serverRequest, serverParams); } function onLoadXMLResponse(xmlResponse:XMLResponse):Void { var discriminator:String = xmlResponse.getProperties()["discriminator"]; var synchListener:SynchListener = SynchListener(xmlResponse.getProperties()["synchListener"]); var featureNodes:Array = XMLTools.getChildNodes(layer.getName(), xmlResponse.firstChild); if (discriminator == "ID") { } else if (discriminator == "ENVELOPE") { if (featureNodes.length != 1) { // EXCEPTION return; } var envelopeNode:XMLNode = XMLTools.getChild("ENVELOPE", XMLNode(featureNodes[0])); if (envelopeNode == null) { trace("Exception: the envelope node for feature \"" + id + "\" is empty."); return; } var envelope:Envelope = null; if (envelopeNode.childNodes.length == 0) { envelope = new NullGeometry(); } else { var minNode:XMLNode = XMLTools.getChild("MIN", envelopeNode); var maxNode:XMLNode = XMLTools.getChild("MAX", envelopeNode); var minX:Number = XMLTools.getNumberValue("X", minNode); var minY:Number = XMLTools.getNumberValue("Y", minNode); var maxX:Number = XMLTools.getNumberValue("X", maxNode); var maxY:Number = XMLTools.getNumberValue("Y", maxNode); envelope = new Envelope(layer.getSRS(), minX, minY, maxX, maxY); } setEnvelope(envelope); layer.onFeatureSetEnvelope(this, synchListener); } else if (discriminator == "PROPERTIES") { if (featureNodes.length != 1) { SelectableLayer(layer).onFeatureResponse(this, false, synchListener); return; } var columnNodes:Array = XMLNode(featureNodes[0]).childNodes; var columnNode:XMLNode = null; var columnName:String = null; var geometry:Geometry = null; // JTMS THAT EVENT WILL FIRE AS LAST for (var i:String in columnNodes) { columnNode = XMLNode(columnNodes[i]); columnName = columnNode.nodeName; //if (columnName == featureType.getIDColumnName()) { // No need to keep the id column value; //} else if (columnName == featureType.getDefaultGeometryProperty().getName()) { var geometryNode:XMLNode = columnNode.firstChild; if (geometryNode == null) { geometry = new NullGeometry(); } else { geometry = GMLFactory.parseGML(geometryNode); } } else if (columnName == "ENVELOPE") { // This is not a valid gml element. } else { // Regular column. if (columnName == "app:time") { // TODO yech!! var val:String = columnNode.firstChild.nodeValue; var vals:Array = val.split(" "); doSetColumnValue(columnName, vals[0] + "T" + vals[1], true); } else { doSetColumnValue(columnName, columnNode.firstChild.nodeValue, true); } } //trace("ownerProp = " + SelectableLayer(layer).getObjectOwnerProperty() + " columnName = " + columnName); if (SelectableLayer(layer).getObjectOwnerProperty() == columnName) { //trace("featureOwner = " + columnNode.firstChild.nodeValue + " user = " + SelectableLayer(layer).getConnector().getUsername()); if (columnNode.firstChild.nodeValue != SelectableLayer(layer).getConnector().getUsername() || columnNode.firstChild.nodeValue == null) { editable = false; } } } idVerified = true; setGeometry(geometry); // JTMS THAT EVENT WILL FIRE AS LAST?????????? SelectableLayer(layer).onFeatureResponse(this, true, synchListener); requestingProperties = false; } } function clone():Feature { var clonedFeature:Feature = new Feature(this.id, this.layer, this.featureType, this.persistent, idVerified); var clonedGeom:Geometry = geometry.clone(); clonedFeature.geometry = clonedGeom; clonedFeature.editingStatus = this.editingStatus; //although private property it is set for(var s:String in columnValues) { clonedFeature.setColumnValue(s, columnValues[s]); } clonedFeature.annotation = this.annotation; return clonedFeature; } private function pushUndo(geometry:Geometry):Void { var featureToSave:Feature = this.clone(); //trace("feature.pushUndo, geom = " + featureToSave.getGeometry().toString()); SelectableLayer(layer).pushUndo(featureToSave); } function undo(undoFeature:Feature):Void { var orgFeature:Feature = SelectableLayer(layer).getFeature(undoFeature.getID()); var undoGeometry:Geometry = undoFeature.getGeometry(); //trace("feature.undo, geom = " + undoGeometry.toString()); orgFeature.editingStatus = undoFeature.getEditingStatus(); orgFeature.setGeometry(undoGeometry); stateEventDispatcher.dispatchEvent(new ChangeEvent(orgFeature, "Feature", "editingStatus", orgFeature.editingStatus)); } function addEventListener(stateEventListener:StateEventListener, sourceClassName:String, actionType:Number, propertyName:String):Void { var type:String = sourceClassName + "_" + actionType + "_" + propertyName; if (type != "Feature_" + StateEvent.CHANGE + "_editingStatus" && type != "Feature_" + StateEvent.CHANGE + "_iconUrl") { // EXCEPTION return; } stateEventDispatcher.addEventListener(stateEventListener, sourceClassName, actionType, propertyName); } function removeEventListener(stateEventListener:StateEventListener, sourceClassName:String, actionType:Number, propertyName:String):Void { if (sourceClassName + "_" + actionType + "_" + propertyName != "Feature_" + StateEvent.CHANGE + "_editingStatus") { // EXCEPTION return; } stateEventDispatcher.removeEventListener(stateEventListener, sourceClassName, actionType, propertyName); } function toString():String { return "Feature(" + id + ")"; } function setEditable(editable:Boolean):Void { this.editable = editable; } function isEditable():Boolean { return editable; } function getColumnValues():Object { var retval:Object = new Object(); for(var s:String in columnValues) { retval[s] = columnValues[s]; } return retval; } function getAnnotation():String { return this.annotation; } function setAnnotation(annotation:String):Void { var sLayer:SelectableLayer = SelectableLayer(getLayer()); if(sLayer != null) { var annotationColumn:String = sLayer.getAnnotationColumn(); if(annotationColumn != null) { columnValues[annotationColumn] = annotation; } } this.annotation = annotation; change(); } }