Merge branch 'master' into snapshotserver
authorAndy Allan <andy@gravitystorm.co.uk>
Wed, 6 Jul 2011 12:21:18 +0000 (13:21 +0100)
committerAndy Allan <andy@gravitystorm.co.uk>
Wed, 6 Jul 2011 12:21:18 +0000 (13:21 +0100)
Conflicts:
net/systemeD/potlatch2/collections/VectorBackgrounds.as

17 files changed:
net/systemeD/halcyon/Map.as
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/XMLBaseConnection.as
net/systemeD/halcyon/connection/XMLConnection.as
net/systemeD/potlatch2/EditController.as
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/collections/VectorBackgrounds.as
net/systemeD/potlatch2/controller/DragSelection.as
net/systemeD/potlatch2/controller/SelectedWay.as
net/systemeD/potlatch2/panels/BackgroundMergeFieldComponent.as [new file with mode: 0644]
net/systemeD/potlatch2/panels/BackgroundMergePanel.mxml [new file with mode: 0644]
net/systemeD/potlatch2/panels/BackgroundPanel.mxml
net/systemeD/potlatch2/utils/BikeShopLoader.as
net/systemeD/potlatch2/utils/BugLoader.as
net/systemeD/potlatch2/utils/SnapshotConnection.as
net/systemeD/potlatch2/utils/SnapshotLoader.as
resources/stylesheets/snapshot.css [new file with mode: 0644]

index 8a45b074955be43ec35add9863ce76306e8dccca..e2148007239b514d5d4a109a1538969e55dcfad0 100644 (file)
@@ -218,13 +218,15 @@ package net.systemeD.halcyon {
             }
                }
 
-        /** Download map data. Data is downloaded for the connection and the vector layers, where supported.
+        /** Download map data. Data is downloaded for the currently visible layers
         * The bounding box for the download is taken from the current map edges.
         */
                public function download():void {
                        this.dispatchEvent(new MapEvent(MapEvent.DOWNLOAD, {minlon:edge_l, maxlon:edge_r, maxlat:edge_t, minlat:edge_b} ));
                        for (var i:uint=0; i<paintContainer.numChildren; i++)
-                               getLayerAt(i).connection.loadBbox(edge_l,edge_r,edge_t,edge_b);
+                               if(getLayerAt(i).visible == true) {
+                    getLayerAt(i).connection.loadBbox(edge_l,edge_r,edge_t,edge_b);
+                }
                }
 
         // Handle mouse events on ways/nodes
index 3e762ff5e854182083c5640ea0c032f94c83d9d4..148f6058243dd58bd84f5378193d50d02e3bc88d 100644 (file)
@@ -13,6 +13,7 @@ package net.systemeD.halcyon.connection {
 
                public var name:String;
                public var statusFetcher:StatusFetcher;
+               public var inlineStatus:Boolean = false;
         protected var apiBaseURL:String;
         protected var policyURL:String;
         protected var params:Object;
@@ -573,6 +574,9 @@ package net.systemeD.halcyon.connection {
 
         // these are functions that the Connection implementation is expected to
         // provide. This class has some generic helpers for the implementation.
+        /**
+        * Load data for the bounding box given. Usually called in response to pan / zoom requests
+        */
                public function loadBbox(left:Number, right:Number,
                                                                top:Number, bottom:Number):void {
            }
index c94a8572b2533e01f1544551babdd4fa52b190f7..28fbae870f29e73fc470d23aac679897540a8a0d 100644 (file)
@@ -108,6 +108,7 @@ package net.systemeD.halcyon.connection {
                                                                           Number(nodeData.@lon),
                                                                           Number(nodeData.@uid),
                                                                           nodeData.@timestamp);
+                if ( inlineStatus ) { newNode.status = nodeData.@status; }
                                
                                        if ( singleEntityRequest ) {
                                                // it's a revert request, so create/update the node
@@ -143,11 +144,13 @@ package net.systemeD.halcyon.connection {
                                                tags = parseTags(data.tag);
                                                if ( way == null ) {
                                                        way=new Way(this, id, version, tags, true, nodelist, uid, timestamp)
+                                                       if ( inlineStatus ) { way.status = data.@status; }
                                                        setWay(way,false);
                                                        createdEntities.push(way);
                                                } else {
                                                        waycount++;
                                                        way.update(version, tags, true, true, nodelist, uid, timestamp);
+                                                       if ( inlineStatus ) { way.status = data.@status; }
                                                        sendEvent(new EntityEvent(NEW_WAY, way), false);
                                                }
                                        }
index 3a4c5ef3bfaedb745ebfa39d1dd1d5f672b1db03..d420da933cf4cb9ef6d7f983b7fc38674790c548 100644 (file)
@@ -13,9 +13,19 @@ package net.systemeD.halcyon.connection {
     /**
     * XMLConnection provides all the methods required to connect to a live
     * OSM server. See OSMConnection for connecting to a read-only .osm file
+    *
+    * @see OSMConnection
     */
        public class XMLConnection extends XMLBaseConnection {
 
+        /**
+        * Create a new XML connection
+        * @param name The name of the connection
+        * @param api The url of the OSM API server, e.g. http://api06.dev.openstreetmap.org/api/0.6/
+        * @param policy The url of the flash crossdomain policy to load,
+                        e.g. http://api06.dev.openstreetmap.org/api/crossdomain.xml
+        * @param initparams Any further parameters for the connection, such as the serverName
+        */
                public function XMLConnection(name:String,api:String,policy:String,initparams:Object) {
 
                        super(name,api,policy,initparams);
index 6afad6e065998ee386e8f868f29b8005dd0e9cb2..9f2243b8ddfcc62eff54cf791eef44a447075bbf 100644 (file)
@@ -45,6 +45,7 @@ package net.systemeD.potlatch2 {
             this._map = map;
             setState(new NoSelection());
             this.tagViewer = tagViewer;
+            this.tagViewer.controller = this;
                        this.toolbox = toolbox;
                        this.toolbox.init(this);
             this.maximiseFunction = Globals.vars.flashvars["maximise_function"];
index f33c39ce34142ed535af5a0c5ba0231f9901599d..a5d43a06bd1d99a25c799e085625fd78da24db61 100644 (file)
     <sidepanel:BackgroundPanel id="backgroundPanelContents" width="100%"/>
   </mx:VBox>
 
+  <!-- merge tags from background layer -->
+
+  <mx:VBox id="backgroundMergePanel" width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox">
+    <sidepanel:BackgroundMergePanel id="backgroundMergePanelContents" width="100%" />
+  </mx:VBox>
+
 </mx:ViewStack>
 
   <mx:Script><![CDATA[
       import net.systemeD.halcyon.connection.*;
       import net.systemeD.halcyon.MapPaint;
+      import net.systemeD.potlatch2.EditController;
       import net.systemeD.potlatch2.mapfeatures.*;
       import net.systemeD.potlatch2.utils.*;
 
       [Bindable] private var editorStack:Container;
 
       public var mapFeatures:MapFeatures;
+      public var controller:EditController;
       private var selectedEntity:Entity;
       private var connection:Connection;
       private var currentCategorySelector:CategorySelector;
               sidebar.selectedChild = tagsPanel;
             }
 
+        } else if (entities.length==2
+                      && xor(entities[0].connection is SnapshotConnection, entities[1].connection is SnapshotConnection)
+                      && xor(!controller.map.getLayerForEntity(entities[0]).isBackground, !controller.map.getLayerForEntity(entities[1]).isBackground) ) {
+            backgroundMergePanelContents.init(entities);
+            sidebar.selectedChild = backgroundMergePanel;
+
                } else if(isMultipleEditable(entities)) {
                        selectedEntity = new EntityCollection(entities);
                        selectedEntity.addEventListener(Connection.TAG_CHANGED, tagChanged);
                UIComponent.resumeBackgroundProcessing();
       }
 
+      private function xor(a:Boolean, b:Boolean):Boolean {
+          return ( a || b) && !(a && b);
+      }
+
       private function refreshFeatureIcon():void {
           var oldFeature:Feature = feature;
           feature = selectedEntity == null ? null : mapFeatures.findMatchingFeature(selectedEntity);
index 6f761f89ff12cfa70887e8f3f947d205049daf16..baa39e885c162f7f6e3ac733d8fd130f0f46fd0a 100644 (file)
@@ -98,7 +98,7 @@ package net.systemeD.potlatch2.collections {
                                                case "SnapshotLoader":
                                                        if (set.url) {
                                                                name ||= 'Snapshot Server'
-                                                               var snapshotLoader:SnapshotLoader = new SnapshotLoader(_map, String(set.url), name);
+                                                               var snapshotLoader:SnapshotLoader = new SnapshotLoader(_map, String(set.url), name, String(set.style));
                                                                if (set.@loaded == "true") {
                                                                        snapshotLoader.load();
                                                                }
index 6f583f606eb1c531e99d86b1f3bb6d8ef5a049e8..941a20dd8863547d4033f9441de7e2cfd3520777 100644 (file)
@@ -3,6 +3,7 @@ package net.systemeD.potlatch2.controller {
        import flash.geom.Point;
     import flash.ui.Keyboard;
     import net.systemeD.potlatch2.EditController;
+    import net.systemeD.halcyon.MapPaint;
     import net.systemeD.halcyon.connection.*;
     import net.systemeD.halcyon.connection.actions.*;
 
@@ -14,6 +15,8 @@ package net.systemeD.potlatch2.controller {
         private var downX:Number;
         private var downY:Number;
                private var dragstate:uint=NOT_MOVED;
+               private var initialMouseEvent:MouseEvent;
+               private var wayList:Array;
                /** Not used? */
                private const NOT_DRAGGING:uint=0;
                
@@ -24,11 +27,13 @@ package net.systemeD.potlatch2.controller {
                private const DRAGGING:uint=2;
         
         /** Start the drag by recording the dragged way, where it started, and when. */
-        public function DragSelection(sel:Array, event:MouseEvent) {
+        public function DragSelection(sel:Array, event:MouseEvent, ways:Array = null) {
             selection = sel.concat();
             downX = event.localX;
             downY = event.localY;
+            wayList = ways;
                        enterTime = (new Date()).getTime();
+                       initialMouseEvent = event;
         }
  
        /** Handle dragging and end drag events. Filters out very short or quick drags. */
@@ -72,6 +77,7 @@ package net.systemeD.potlatch2.controller {
 
                /** Abort dragging if ESC pressed. */
                override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
+            if (event.keyCode==Keyboard.SPACE) { return cycleAllWays(initialMouseEvent); }
                        if (event.keyCode==Keyboard.ESCAPE) {
                                for each (var entity:Entity in selection) {
                                        entity.dispatchEvent(new EntityDraggedEvent(Connection.ENTITY_DRAGGED, entity, 0, 0));
@@ -92,6 +98,26 @@ package net.systemeD.potlatch2.controller {
                        dragstate=NOT_MOVED;
                }
 
+        private function cycleAllWays(event:MouseEvent):ControllerState {
+            trace("cycleAllWays");
+            if (!(downX && downY) || (wayList && wayList.length<2) || selection.length != 1) { return this; }
+            trace("cycleAllWays2");
+            if (!wayList) {
+                wayList=selection
+                for each (var l:MapPaint in controller.map.getLayers()) {
+                    trace("Layer "+l.connection.name);
+                    trace(l.findWaysAtPoint(event.stageX, event.stageY).length);
+                    wayList = wayList.concat(l.findWaysAtPoint(event.stageX, event.stageY,selection[0]));
+                }
+            }
+            for each (var way:Way in wayList) {
+                trace(way + "::" + way.connection.name);
+            }
+            wayList=wayList.slice(1).concat(wayList[0]);
+            // Find the new way's index of the currently "selected" node, to facilitate keyboard navigation
+            return new DragSelection([wayList[0]], event, wayList);
+        }
+
         /** Highlight the dragged selection. */
         override public function enterState():void {
                        for each (var entity:Entity in selection) {
index 192e2417f710d4fe44a85316c1c6180fa7eff47a..d23c28df432f69f28ad5c5ed95290c9b62d1186b 100644 (file)
@@ -58,7 +58,7 @@ package net.systemeD.potlatch2.controller {
                 var d:DragWayNode=new DragWayNode(firstSelected as Way, -1, event, true);
                                d.forceDragStart();
                                return d;
-                       } else if ( event.type == MouseEvent.MOUSE_DOWN && event.ctrlKey && entity && entity!=firstSelected && paint==layer) {
+                       } else if ( event.type == MouseEvent.MOUSE_DOWN && event.ctrlKey && entity && entity!=firstSelected) {
                                // multiple selection
                                return new SelectedMultiple([firstSelected,entity],layer);
                        } else if ( event.type == MouseEvent.MOUSE_UP && focus==firstSelected ) {
diff --git a/net/systemeD/potlatch2/panels/BackgroundMergeFieldComponent.as b/net/systemeD/potlatch2/panels/BackgroundMergeFieldComponent.as
new file mode 100644 (file)
index 0000000..39d226d
--- /dev/null
@@ -0,0 +1,24 @@
+package net.systemeD.potlatch2.panels {
+
+    import mx.controls.Label;
+    import mx.controls.dataGridClasses.*;
+    import net.systemeD.potlatch2.panels.BackgroundMergePanel;
+
+    public class BackgroundMergeFieldComponent extends Label {
+
+     override public function set data(value:Object):void
+     {
+        if(value != null)
+        {
+            super.data = value;
+            if (listData.label != ' ') { // yes, a space. No, neither null nor empty string. I hate you, adobe
+                textField.background = true;
+                textField.backgroundColor = BackgroundMergePanel(listData.owner.parent).getColorFor(listData.rowIndex);
+            } else {
+                textField.background = false;
+            }
+        }
+     }
+  }
+
+}
\ No newline at end of file
diff --git a/net/systemeD/potlatch2/panels/BackgroundMergePanel.mxml b/net/systemeD/potlatch2/panels/BackgroundMergePanel.mxml
new file mode 100644 (file)
index 0000000..32f6500
--- /dev/null
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Background Merge Panel
+-->
+
+<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" height="100%">
+  <mx:Text id="backgroundMergePanelText" text="W00t!!!" width="100%" styleName="helpInfo" />
+  <mx:DataGrid editable="false" id="backgroundPanelDG" width="100%" height="50%">
+    <mx:columns>
+      <mx:DataGridColumn editable="false" dataField="k" headerText="Key" />
+      <mx:DataGridColumn editable="false" dataField="e" headerText="OSM value" />
+      <mx:DataGridColumn editable="false" headerText="Merge" width="50" textAlign="center">
+        <mx:itemRenderer>
+          <mx:Component>
+            <mx:HBox horizontalAlign="center" verticalAlign="middle">
+              <mx:Button label="&lt;&lt;" visible="{parentDocument.buttonVisible(data.b, data.e)}" click="parentDocument.mergeForKey(data.k);" />
+            </mx:HBox>
+          </mx:Component>
+        </mx:itemRenderer>
+      </mx:DataGridColumn>
+      <mx:DataGridColumn editable="false" itemRenderer="net.systemeD.potlatch2.panels.BackgroundMergeFieldComponent" dataField="b" headerText="Background value" />
+    </mx:columns>
+  </mx:DataGrid>
+  <mx:ViewStack id="statusStack" resizeToContent="true" width="100%">
+    <mx:VBox id="empty" />
+    <mx:VBox id="not_complete">
+      <mx:Text text="All the data copied to the main layer? Click 'complete'!" />
+      <mx:Button label="Complete" click="markComplete()"/>
+    </mx:VBox>
+    <mx:VBox id="complete">
+      <mx:Text text="This feature has been marked as complete. If this is wrong, press the button below." />
+      <mx:Button label="Not complete" click="markNotComplete()"/>
+    </mx:VBox>
+  </mx:ViewStack>
+  <mx:Script><![CDATA[
+
+      import net.systemeD.halcyon.connection.*;
+      import net.systemeD.halcyon.MapPaint;
+      import net.systemeD.potlatch2.utils.SnapshotConnection;
+
+      import mx.collections.*;
+
+      private var editableEntity:Entity;
+      private var backgroundEntity:Entity;
+      private var tagDataProvider:ArrayCollection;
+
+      public function init(entities:Array):void {
+          if ( tagDataProvider == null ) {
+              tagDataProvider = new ArrayCollection();
+              backgroundPanelDG.dataProvider = tagDataProvider;
+          }
+
+          if (parentDocument.controller.map.getLayerForEntity(entities[0]).isBackground) {
+              backgroundEntity = entities[0];
+              editableEntity = entities[1];
+          } else {
+              backgroundEntity = entities[1];
+              editableEntity = entities[0];
+          }
+          backgroundEntity.addEventListener(Connection.STATUS_CHANGED, statusEvent, false, 0, true);
+          setStatusStack();
+          editableEntity.addEventListener(Connection.TAG_CHANGED, tagChanged, false, 0, true);
+          updateTagDataProvider();
+      }
+
+      private function updateTagDataProvider():void {
+          var tag:Tag;
+          var keys:Array = [];
+
+          tagDataProvider.removeAll();
+
+          for each (tag in backgroundEntity.getTagArray() ) {
+              keys.push(tag.key);
+          }
+
+          for each (tag in editableEntity.getTagArray() ) {
+              keys.push(tag.key);
+          }
+
+          keys=keys.filter(function(k:*, i:int, arr:Array):Boolean { return arr.indexOf(k) == i } ); // remove duplicates
+          keys.sort();
+
+          for each (var key:String in keys) {
+              tagDataProvider.addItem({k:key, e:editableEntity.getTag(key), b:backgroundEntity.getTag(key)});
+          }
+          backgroundPanelDG.invalidateList();
+      }
+
+      private function tagChanged(e:Event):void {
+          updateTagDataProvider();
+      }
+
+      public function buttonVisible(b:String, e:String):Boolean {
+          if (b != null && b != e) {
+              return true;
+          }
+          return false;
+      }
+
+      public function mergeForKey(key:String):void {
+          editableEntity.setTag(key, backgroundEntity.getTag(key), MainUndoStack.getGlobalStack().addAction);
+      }
+
+      // Don't call this for things you don't want coloured in. Like for empty tags.
+      public function getColorFor(i:int):int {
+          if (tagDataProvider[i].e == tagDataProvider[i].b) {
+              return 0xDDFFDD; // matching, green
+           } else if (tagDataProvider[i].e == null) {
+              return 0xDDDDFF; // new value, blue
+           } else if (tagDataProvider[i].b) {
+              return 0xFFDDDD; // conflicting, red
+           }
+           return NaN; // which is interpretted as black
+      }
+
+      private function statusEvent(e:Event):void {
+            setStatusStack();
+      }
+
+      private function setStatusStack():void {
+          switch (backgroundEntity.status) {
+              case 'incomplete':
+                  statusStack.selectedChild = not_complete;
+                  break;
+              case 'complete':
+                  statusStack.selectedChild = complete;
+                  break;
+              default:
+                  statusStack.selectedChild = empty;
+          }
+      }
+
+      private function markComplete():void {
+          if (backgroundEntity.connection is SnapshotConnection) {
+              SnapshotConnection(backgroundEntity.connection).markComplete(backgroundEntity);
+          }
+      }
+
+      private function markNotComplete():void {
+          if (backgroundEntity.connection is SnapshotConnection) {
+              SnapshotConnection(backgroundEntity.connection).markNotComplete(backgroundEntity);
+          }
+      }
+  ]]>
+  </mx:Script>
+</mx:VBox>
\ No newline at end of file
index 982feb2425862041f1fc456c776b9c1498cc89dc..aaa44df0eb78916d52e6d16a4a753f2a57ae318d 100644 (file)
       <mx:DataGridColumn editable="false" dataField="value" headerText="value" />
     </mx:columns>
   </mx:DataGrid>
-  <mx:Text text="All the data copied to the main layer? Click 'complete'!" />
-  <mx:Button label="Complete" click="markComplete()"/>
+  <mx:ViewStack id="statusStack" resizeToContent="true" width="100%">
+    <mx:VBox id="empty" />
+    <mx:VBox id="not_complete">
+      <mx:Text text="All the data copied to the main layer? Click 'complete'!" />
+      <mx:Button label="Complete" click="markComplete()"/>
+    </mx:VBox>
+    <mx:VBox id="complete">
+      <mx:Text text="This feature has been marked as complete. If this is wrong, press the button below." />
+      <mx:Button label="Not complete" click="markNotComplete()"/>
+    </mx:VBox>
+  </mx:ViewStack>
   <mx:Script><![CDATA[
 
       import net.systemeD.halcyon.connection.*;
       private var layer:MapPaint;
 
       public function init(entity:Entity, layer:MapPaint):void {
-            this.layer = layer;
-            if ( tagDataProvider == null ) {
-                tagDataProvider = new ArrayCollection();
-                backgroundPanelDG.dataProvider = tagDataProvider;
-            }
-
-            selectedEntity=entity;
-            updateTagDataProvider();
+          this.layer = layer;
+          if ( tagDataProvider == null ) {
+              tagDataProvider = new ArrayCollection();
+              backgroundPanelDG.dataProvider = tagDataProvider;
+          }
+
+          selectedEntity=entity;
+          selectedEntity.addEventListener(Connection.STATUS_CHANGED, statusEvent, false, 0, true);
+          setStatusStack();
+          updateTagDataProvider();
       }
 
       private function updateTagDataProvider():void {
-            tagDataProvider.removeAll();
-            if (selectedEntity==null) { return; }
-            var tags:Array = selectedEntity.getTagArray();
-            tags.sortOn("key");
-            for each(var tag:Tag in tags) { tagDataProvider.addItem(tag); }
+          tagDataProvider.removeAll();
+          if (selectedEntity==null) { return; }
+          var tags:Array = selectedEntity.getTagArray();
+          tags.sortOn("key");
+          for each(var tag:Tag in tags) { tagDataProvider.addItem(tag); }
+      }
+
+      private function statusEvent(e:Event):void {
+            setStatusStack();
+      }
+
+      private function setStatusStack():void {
+          switch (selectedEntity.status) {
+              case 'incomplete':
+                  statusStack.selectedChild = not_complete;
+                  break;
+              case 'complete':
+                  statusStack.selectedChild = complete;
+                  break;
+              default:
+                  statusStack.selectedChild = empty;
+          }
       }
 
       private function markComplete():void {
-            if (selectedEntity.connection is SnapshotConnection) {
-                SnapshotConnection(selectedEntity.connection).markComplete(selectedEntity);
-            }
+          if (selectedEntity.connection is SnapshotConnection) {
+              SnapshotConnection(selectedEntity.connection).markComplete(selectedEntity);
+          }
+      }
+
+      private function markNotComplete():void {
+          if (selectedEntity.connection is SnapshotConnection) {
+              SnapshotConnection(selectedEntity.connection).markNotComplete(selectedEntity);
+          }
       }
       ]]>
   </mx:Script>
index 87996f3ec65a524f485952bd0a81ae8282b89d7e..003e671f7eff11de48b5c0f0610844f93f94d10a 100644 (file)
@@ -34,12 +34,12 @@ package net.systemeD.potlatch2.utils {
             this.bikeShopBaseURL = url;
             this.name = name;
             this.connection = new BikeShopConnection(name,url,bikeShopBaseURL+"crossdomain.xml",null);
+            _layer = map.addLayer(connection, STYLESHEET);
+            _layer.visible = false;
         }
 
         public function load():void {
-            if (!_layer) {
-                _layer = map.addLayer(connection, STYLESHEET);
-            }
+            _layer.visible = true;
             connection.loadBbox(map.edge_l, map.edge_r, map.edge_t, map.edge_b);
         }
     }
index 4c6f08bb12eebc200a23a2761c9c2fd38173e178..d9caabcb1b1ff0199673720523bb80851a4347c7 100644 (file)
@@ -27,12 +27,12 @@ package net.systemeD.potlatch2.utils {
             this.name = name;
             this.bugDetailsURL = details;
             connection = new BugConnection(name, url, bugApiKey, details);
+            _layer = map.addLayer(connection, STYLESHEET, true, true);
+            _layer.visible = false;
         }
 
         public function load():void {
-            if (!_layer) {
-                _layer = map.addLayer(connection, STYLESHEET, true, true);
-            }
+            _layer.visible = true;
             connection.loadBbox(map.edge_l, map.edge_r, map.edge_t, map.edge_b);
         }
     }
index 4910cf5fd1790b511b52ceac965f78fe95a0482b..a374a0651c74fd993b554166f4256443e69c2063 100644 (file)
@@ -4,33 +4,74 @@ package net.systemeD.potlatch2.utils {
     import flash.events.Event;
     import flash.net.*;
 
+    /**
+    * A connection to a Snapshot server. A Snapshot server serves OSM map requests and can also
+    * track the "status" of an entity. Most other types of XMLConnection requests will fail. See
+    * http://www.github.com/gravitystorm/snapshot-server for example code based on the database
+    * structure created by osmosis pgsnapshot schema.
+    */
+
     public class SnapshotConnection extends XMLConnection {
 
         public function SnapshotConnection(cname:String,api:String,policy:String,initparams:Object=null) {
             super(cname,api,policy,initparams);
+            inlineStatus = true;
         }
 
-        /** Send a "complete" call to the server, and remove it from the current layer */
+        // As it stands, the following two functions could be refactored further.
+
+        /**
+        * Post a status update call to the server and update entity.status if successful.
+        */
         public function markComplete(entity:Entity):void {
+            var urlReq:URLRequest;
+
+            if (entity is Node) {
+                var node:Node = Node(entity);
+                if (node == getNode(node.id)) { // confirm it's from this connection
+                    makeRequest(entity, 'complete');
+                }
+
+            } else if (entity is Way) {
+                var way:Way = Way(entity);
+                if (way == getWay(way.id)) { // confirm it's from this connection
+                    makeRequest(entity, 'complete');
+                }
+            }
+        }
+
+        /**
+        * Send a "complete" call to the server and update entity.status if successful.
+        */
+        public function markNotComplete(entity:Entity):void {
+            var urlReq:URLRequest;
+
             if (entity is Node) {
-              var node:Node = Node(entity);
-              if (node == getNode(node.id)) { // confirm it's from this connection
-                  var urlReq:URLRequest = new URLRequest(apiBaseURL+"node/"+node.id+"/complete");
-                  urlReq.method = "POST";
-                  urlReq.data = '   ';
-                  urlReq.contentType = "application/xml";
-                  urlReq.requestHeaders = [ new URLRequestHeader("X_HTTP_METHOD_OVERRIDE", "PUT"),
-                                            new URLRequestHeader("X-Error-Format", "XML") ];
-                  var loader:URLLoader = new URLLoader();
-                  loader.addEventListener(Event.COMPLETE, function(e:Event):void { killNode(node.id) });
-                  loader.load(urlReq);
-              }
+                var node:Node = Node(entity);
+                if (node == getNode(node.id)) { // confirm it's from this connection
+                    makeRequest(entity, 'incomplete');
+                }
 
             } else if (entity is Way) {
-              var way:Way = Way(entity);
-              trace("not implemented");
+                var way:Way = Way(entity);
+                if (way == getWay(way.id)) { // confirm it's from this connection
+                    makeRequest(entity, 'incomplete');
+                }
             }
         }
 
+        private function makeRequest(entity:Entity, status:String):void {
+            var urlReq:URLRequest = new URLRequest(apiBaseURL+entity.getType()+"/"+entity.id+"/status");
+            urlReq.method = "POST";
+            urlReq.data = status;
+            var loader:URLLoader = new URLLoader();
+            loader.addEventListener(Event.COMPLETE, function(e:Event):void { updateStatus(entity, status) });
+            loader.load(urlReq);
+        }
+
+        private function updateStatus(e:Entity, s:String):void {
+            e.setStatus(s);
+        }
+
     }
 }
\ No newline at end of file
index b0feaed1b88f5d730e1fb625caebaaeea4bec1f4..e2457a36a12e7f5378f3ec2887b13e8f7582a016 100644 (file)
@@ -4,23 +4,43 @@ package net.systemeD.potlatch2.utils {
     import net.systemeD.halcyon.MapPaint;
     import net.systemeD.potlatch2.utils.SnapshotConnection;
 
+    /**
+    * Loads a Snapshot layer. Uses lazy-loading such that only when the load() function is
+    * called will the layer be created and added to the map
+    *
+    * @see SnapShotConnection
+    */
     public class SnapshotLoader {
 
         private var map:Map;
         private var _layer:MapPaint;
-        private static const STYLESHEET:String="stylesheets/wireframe.css"; //TODO take from xml
+        private static const STYLESHEET:String="stylesheets/snapshot.css";
         private var connection:SnapshotConnection;
+        private var _stylesheet:String;
 
-
-        public function SnapshotLoader(map:Map, url:String, name:String):void {
+        /**
+        * Create a new SnapshotLoader
+        * @param map The map object to attach the layer to
+        * @param url The url of the snapshot server. This should be to the api base and
+                     end in a forward slash, e.g. http://example.com/snapshot/api/
+        * @param name The name to give to the layer/connection
+        * @param stylesheet The url of the stylesheet to use for styling the layer
+        */
+        public function SnapshotLoader(map:Map, url:String, name:String, stylesheet:String = null):void {
             this.map = map;
             connection = new SnapshotConnection(name, url, '');
+            _stylesheet = (stylesheet && stylesheet != '') ? stylesheet : STYLESHEET;
+            _layer = map.addLayer(connection, _stylesheet, true, true);
+            _layer.visible = false;
         }
 
+        /**
+        * Load the layer.
+        * Call this the first time you wish to load the layer. After this it will respond
+        * automatically to pan / zooming of the associated Map
+        */
         public function load():void {
-            if (!_layer) {
-                _layer = map.addLayer(connection, STYLESHEET);
-            }
+            _layer.visible = true;
             connection.loadBbox(map.edge_l, map.edge_r, map.edge_t, map.edge_b);
         }
     }
diff --git a/resources/stylesheets/snapshot.css b/resources/stylesheets/snapshot.css
new file mode 100644 (file)
index 0000000..324e131
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+
+       Stylesheet that does simple wireframe display
+
+*/
+
+way :hover     { z-index: 2; width: 1; color: yellow; }
+way :selected { z-index: 2; width: 2; color: yellow; }
+way !:drawn { z-index:10; width: 1; color: black; }
+
+node :selectedway { z-index: 8; icon-image: square; icon-width: 6; color: green; }
+node :hoverway { z-index: 9; icon-image: square; icon-width: 6; color: blue; }
+node :selected { z-index: 9; icon-image: square; icon-width: 6; color: red; casing-color: black; casing-width: 1; }
+node !:drawn :poi { z-index: 2; icon-image: square; icon-width: 4; color: green; casing-color: black; casing-width: 1; }
+node !:drawn :hasTags { z-index: 9; icon-image: circle; icon-width: 3; color: black; }
+node :hasTags :selectedway { z-index: 9; icon-image: square; icon-width: 8; color: black; layer: 5; }
+
+way[_status=incomplete]::statushighlight { z-index: 0; width: 10; color: #d95f02; }
+way[_status=complete]::statushighlight { z-index: 0; width: 10; color: #1b9e77; opacity: 0.4; }
+node[_status=incomplete]::statushighlight { z-index: 0; icon-image: square; icon-width: 10; color: #d95f02; }
+node[_status=complete]::statushighlight { z-index: 0; icon-image: square; icon-width: 10; color: #1b9e77; opacity: 0.4; }
+
+
+