initial relation editing
authorDave Stubbs <osm@randomjunk.co.uk>
Tue, 29 Dec 2009 18:54:34 +0000 (18:54 +0000)
committerDave Stubbs <osm@randomjunk.co.uk>
Tue, 29 Dec 2009 18:54:34 +0000 (18:54 +0000)
16 files changed:
net/systemeD/halcyon/connection/Relation.as
net/systemeD/halcyon/connection/XMLConnection.as
net/systemeD/halcyon/styleparser/MapCSS.as
net/systemeD/potlatch2/RelationEditorPanel.mxml [new file with mode: 0644]
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/mapfeatures/EditorFactory.as
net/systemeD/potlatch2/mapfeatures/Feature.as
net/systemeD/potlatch2/mapfeatures/editors/RelationMemberEditor.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/RelationMemberEditorFactory.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/RouteEditor.mxml [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/RouteEditorFactory.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/RouteIcon.mxml [new file with mode: 0644]
resources/features/cycle__lcn.png [new file with mode: 0644]
resources/features/cycle__ncn.png [new file with mode: 0644]
resources/features/cycle__rcn.png [new file with mode: 0644]
resources/map_features.xml

index 5739750..22474ad 100644 (file)
@@ -21,6 +21,15 @@ package net.systemeD.halcyon.connection {
             return members.length;
         }
 
+        public function findEntityMemberIndex(entity:Entity):int {
+            for (var index:uint = 0; index < members.length; index++) {
+                var member:RelationMember = members[index];
+                if ( member.entity == entity )
+                    return index;
+            }
+            return -1;
+        }
+        
         public function getMember(index:uint):RelationMember {
             return members[index];
         }
@@ -49,6 +58,11 @@ package net.systemeD.halcyon.connection {
                public override function getType():String {
                        return 'relation';
                }
+               
+               public override function toString():String {
+            return "Relation("+id+"@"+version+"): "+members.length+" members "+getTagList();
+        }
+
     }
 
 }
index dbe9fdb..cff6efc 100644 (file)
@@ -32,6 +32,8 @@ package net.systemeD.halcyon.connection {
 
             var mapLoader:URLLoader = new URLLoader();
             mapLoader.addEventListener(Event.COMPLETE, loadedMap);
+            mapLoader.addEventListener(IOErrorEvent.IO_ERROR, errorOnMapLoad);
+            mapLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, mapLoadStatus);
             mapLoader.load(mapRequest);
             dispatchEvent(new Event(LOAD_STARTED));
                }
@@ -43,6 +45,13 @@ package net.systemeD.halcyon.connection {
             return tags;
         }
 
+        private function errorOnMapLoad(event:Event):void {
+            trace("error loading map");
+        }
+        private function mapLoadStatus(event:HTTPStatusEvent):void {
+            trace("loading map status = "+event.status);
+        }
+        
         private function loadedMap(event:Event):void {
             dispatchEvent(new Event(LOAD_COMPLETED));
 
@@ -56,11 +65,14 @@ package net.systemeD.halcyon.connection {
                 version = uint(nodeData.@version);
 
                 var node:Node = getNode(id);
-                if ( node == null ) {
+                if ( node == null || !node.loaded ) {
                     var lat:Number = Number(nodeData.@lat);
                     var lon:Number = Number(nodeData.@lon);
                     tags = parseTags(nodeData.tag);
-                    setNode(new Node(id, version, tags, true, lat, lon),false);
+                    if ( node == null )
+                        setNode(new Node(id, version, tags, true, lat, lon),false);
+                    else
+                        node.update(version, tags, true, lat, lon);
                 }
             }
 
@@ -69,12 +81,59 @@ package net.systemeD.halcyon.connection {
                 version = uint(data.@version);
 
                 var way:Way = getWay(id);
-                if ( way == null ) {
+                if ( way == null || !way.loaded ) {
                     var nodes:Array = [];
                     for each(var nd:XML in data.nd)
                         nodes.push(getNode(Number(nd.@ref)));
                     tags = parseTags(data.tag);
-                    setWay(new Way(id, version, tags,true,  nodes),false);
+                    if ( way == null )
+                        setWay(new Way(id, version, tags, true, nodes),false);
+                    else
+                        way.update(version, tags, true, nodes);
+                }
+            }
+            
+            for each(var relData:XML in map.relation) {
+                id = Number(relData.@id);
+                version = uint(relData.@version);
+                
+                var rel:Relation = getRelation(id);
+                if ( rel == null || !rel.loaded ) {
+                    tags = parseTags(relData.tag);
+                    var members:Array = [];
+                    for each(var memberXML:XML in relData.member) {
+                        var type:String = memberXML.@type.toLowerCase();
+                        var role:String = memberXML.@role;
+                        var memberID:Number = Number(memberXML.@ref);
+                        var member:Entity = null;
+                        if ( type == "node" ) {
+                            member = getNode(memberID);
+                            if ( member == null ) {
+                                member = new Node(memberID,0,{},false,0,0);
+                                setNode(Node(member),true);
+                            }
+                        } else if ( type == "way" ) {
+                            member = getWay(memberID);
+                            if (member == null) {
+                                member = new Way(memberID,0,{},false,[]);
+                                setWay(Way(member),true);
+                            }
+                        } else if ( type == "relation" ) {
+                            member = getRelation(memberID);
+                            if (member == null) {
+                                member = new Relation(memberID,0,{},false,[]);
+                                setRelation(Relation(member),true);
+                            }
+                        }
+                        
+                        if ( member != null )
+                            members.push(new RelationMember(member, role));
+                    }
+                    
+                    if ( rel == null )
+                        setRelation(new Relation(id, version, tags, true, members), false);
+                    else
+                        rel.update(version,tags,true,members);
                 }
             }
             
index 780c1b4..5039c08 100755 (executable)
@@ -349,8 +349,7 @@ package net.systemeD.halcyon.styleparser {
                                // Parse properties
                                // ** also do units, e.g. px/pt
                                if (a.match(COLOR)) {
-                                       if (CSSCOLORS[t[a].toLowerCase()]) { t[a]=CSSCOLORS[t[a].toLowerCase()]; }
-                                       else if ((o=HEX.exec(t[a]))) { t[a]=Number("0x"+o[1]); }
+                                       t[a] = parseCSSColor(t[a]);
                                }
                                
                                // Set in styles
@@ -394,5 +393,16 @@ package net.systemeD.halcyon.styleparser {
                        return null;
                }
 
+        public static function parseCSSColor(colorStr:String):uint {
+            colorStr = colorStr.toLowerCase();
+            if (CSSCOLORS[colorStr])
+                return CSSCOLORS[colorStr];
+            else {
+                var match:Object = HEX.exec(colorStr);
+                if ( match )
+                    return Number("0x"+match[1]);
+            }
+            return 0;
+        }
        }
 }
diff --git a/net/systemeD/potlatch2/RelationEditorPanel.mxml b/net/systemeD/potlatch2/RelationEditorPanel.mxml
new file mode 100644 (file)
index 0000000..9c602f6
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mx:TitleWindow
+       xmlns:mx="http://www.adobe.com/2006/mxml"
+       xmlns:potlatch2="net.systemeD.potlatch2.*"
+       title="Edit Relation" width="350" height="400"
+       showCloseButton="true" close="PopUpManager.removePopUp(this);">
+       
+       <potlatch2:TagViewer width="100%" height="100%" id="tagViewer"
+           creationComplete="checkRelation()"/>
+       
+       <mx:Script><![CDATA[
+               import net.systemeD.halcyon.*;
+               import net.systemeD.halcyon.connection.*;
+               import net.systemeD.potlatch2.*;
+               import mx.managers.PopUpManager;
+
+        private var _relation:Relation;
+        
+               public function setRelation(relation:Relation):void {
+            _relation = relation;
+//            if ( tagViewer != null )
+//                tagViewer.setEntity(_relation);
+               }
+
+        public function checkRelation():void {
+            if ( _relation != null )
+                tagViewer.setEntity(_relation);
+        }
+    ]]></mx:Script>    
+</mx:TitleWindow>
+
index bdcf17e..ab2a04f 100644 (file)
@@ -3,7 +3,7 @@
        xmlns:mx="http://www.adobe.com/2006/mxml"
        xmlns:flexlib="flexlib.containers.*"
     backgroundColor="white"
-    creationComplete="loadFeatures()">
+    initialize="loadFeatures()">
 
   <mx:ViewStack id="stack" width="100%" height="100%">
   <mx:VBox width="100%" height="100%" label="Simple">
@@ -25,7 +25,7 @@
       <mx:htmlText><![CDATA[<i>No Selection</i>]]></mx:htmlText>
     </mx:Label>
 
-    <mx:DataGrid editable="true" width="100%" height="100%" id="advancedTagGrid">
+    <mx:DataGrid editable="true" width="100%" height="75%" id="advancedTagGrid">
             <mx:columns>
                 <mx:DataGridColumn editable="true" dataField="key" headerText="Key"/>
                 <mx:DataGridColumn editable="true" dataField="value" headerText="Value"/>
       <mx:LinkButton label="Delete" click="removeTag()"/>
       <mx:LinkButton label="Add" click="addNewTag()"/>
     </mx:HBox>
+    
+    <mx:DataGrid editable="true" width="100%" height="25%" id="relationsGrid"
+        doubleClickEnabled="true"
+        itemDoubleClick="editRelation(ListEvent(event).rowIndex)">
+        <mx:columns>
+            <mx:DataGridColumn editable="false" dataField="description" headerText="Relation"/>
+            <mx:DataGridColumn editable="false" dataField="id" headerText="ID"/>
+            <mx:DataGridColumn editable="true" dataField="role" headerText="Role"/>
+        </mx:columns>
+    </mx:DataGrid>
+
+    <mx:HBox horizontalAlign="right" width="100%">
+      <mx:LinkButton label="Remove from" click="removeFromRelation()"/>
+      <mx:LinkButton label="Add to" click="addToRelation()"/>
+    </mx:HBox>
+
   </mx:VBox>
 
   </mx:ViewStack>
@@ -55,7 +71,7 @@
       
       private var mapFeatures:MapFeatures;
       private var selectedEntity:Entity;
-      private var collection:ArrayCollection;
+      private var tagDataProvider:ArrayCollection;
       private var tw:CategorySelector = null;
       private var feature:Feature = null;
 
       }
 
       private function setupAdvanced(entity:Entity):void {
-          if ( collection == null ) {
-              collection = new ArrayCollection();
-              advancedTagGrid.dataProvider = collection;
+          if ( tagDataProvider == null ) {
+              tagDataProvider = new ArrayCollection();
+              advancedTagGrid.dataProvider = tagDataProvider;
           }
 
-          collection.removeAll();
+          tagDataProvider.removeAll();
           
           if ( entity == null ) {
               advancedID.htmlText = "";
               var tags:Array = entity.getTagArray();
               tags.sortOn("key");
               for each(var tag:Tag in tags)
-                  collection.addItem(tag);
+                  tagDataProvider.addItem(tag);
+          }
+          
+          if ( entity == null ) {
+              relationsGrid.dataProvider = null;
+          } else {
+              var relations:Array = [];
+              for each( var rel:Relation in entity.parentRelations ) {
+                  var props:Object = {};
+                  props["relation"] = rel;
+                  props["id"] = rel.id;
+                  var memberIndex:uint = rel.findEntityMemberIndex(entity);
+                  props["role"] = rel.getMember(memberIndex).role;
+                  
+                  var desc:String = "";
+                  var relTags:Object = rel.getTagsHash();
+                  if ( relTags["type"] ) {
+                      desc = relTags["type"];
+                      if ( relTags[desc] )
+                          desc += " " + relTags[desc];
+                  }
+                  if ( relTags["ref"] )
+                      desc += " " + relTags["ref"];
+                  if ( relTags["name"] )
+                      desc += " " + relTags["name"];
+                  props["description"] = desc;
+                  
+                  relations.push(props);
+              }
+              relationsGrid.dataProvider = relations;
           }
       }
 
+      private function editRelation(index:uint):void {
+          trace("edit relation "+index+" "+selectedEntity.parentRelations[index]);
+          var panel:RelationEditorPanel = RelationEditorPanel(
+              PopUpManager.createPopUp(Application(Application.application), RelationEditorPanel, true));
+          panel.setRelation(selectedEntity.parentRelations[index]);
+          PopUpManager.centerPopUp(panel);
+      }
+      
       private function tagChanged(event:TagEvent):void {
           refreshFeatureIcon();
           
-          if ( collection != null ) {
+          if ( tagDataProvider != null ) {
               // check to see if the key is already in our list
               var exists:Boolean = false;
               var tag:Tag = null;
               var i:uint;
-              for ( i = 0; i < collection.length && !exists; i++ ) {
-                  tag = Tag(collection.getItemAt(i));
+              for ( i = 0; i < tagDataProvider.length && !exists; i++ ) {
+                  tag = Tag(tagDataProvider.getItemAt(i));
                   exists = tag.key == event.key;
               }
               if ( !exists ) {
                   tag = new Tag(selectedEntity, event.key, event.newValue);
-                  collection.addItem(tag);
-                  collection.refresh();
+                  tagDataProvider.addItem(tag);
+                  tagDataProvider.refresh();
               } else {
                   if ( event.newValue == null ) {
-                      collection.removeItemAt(i-1);
-                      collection.refresh();
+                      tagDataProvider.removeItemAt(i-1);
+                      tagDataProvider.refresh();
                   } else {
-                      collection.itemUpdated(tag, "value");
+                      tagDataProvider.itemUpdated(tag, "value");
                   }
               }
           }
       public function addNewTag():void {
           var newKey:String = "(new tag)";
           var newTag:Tag = new Tag(selectedEntity, newKey, "(new value)");
-          collection.addItem(newTag);
-          advancedTagGrid.editedItemPosition = {rowIndex: collection.getItemIndex(newTag), columnIndex: 0};
+          tagDataProvider.addItem(newTag);
+          advancedTagGrid.editedItemPosition = {rowIndex: tagDataProvider.getItemIndex(newTag), columnIndex: 0};
       }
 
       public function removeTag():void {
           selectedEntity.setTag(k, null);
       }
       
+      public function addToRelation():void {
+      }
+      
+      public function removeFromRelation():void {
+      }
+      
       public function initFeatureBox():void {
           tw = new CategorySelector();
           tw.addEventListener("selectedType", changeFeatureType);
index 3b8a002..eae6031 100644 (file)
@@ -18,6 +18,7 @@ package net.systemeD.potlatch2.mapfeatures {
             case "freetext": return new FreeTextEditorFactory(inputXML);
             case "choice": return new ChoiceEditorFactory(inputXML);
             case "speed": return new SpeedEditorFactory(inputXML);
+            case "route": return new RouteEditorFactory(inputXML);
             
             }
             
index e24c827..714d9c3 100644 (file)
@@ -63,6 +63,7 @@ package net.systemeD.potlatch2.mapfeatures {
                 _editors.push(editor);
             }
         }
+        
         public function get editors():Array {
             return _editors;
         }
@@ -88,9 +89,13 @@ package net.systemeD.potlatch2.mapfeatures {
         
         public function htmlDetails(entity:Entity):String {
             var icon:XMLList = _xml.icon;
+            return makeHTMLIcon(icon, entity);
+        }   
+
+        public static function makeHTMLIcon(icon:XMLList, entity:Entity):String {
             if ( icon == null )
                 return "";
-
+            
             var txt:String = icon.children().toXMLString();
             var replaceTag:Function = function():String {
                 var value:String = entity.getTag(arguments[1]);
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/RelationMemberEditor.as b/net/systemeD/potlatch2/mapfeatures/editors/RelationMemberEditor.as
new file mode 100644 (file)
index 0000000..b8fd174
--- /dev/null
@@ -0,0 +1,68 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import mx.containers.VBox;
+    import flash.events.*;
+
+       public class RelationMemberEditor extends VBox {
+
+      protected var _factory:RelationMemberEditorFactory;
+      protected var _entity:Entity;
+      
+      [Bindable(event="factory_set")]
+      public function get fieldName():String {
+          return _factory == null ? "" : _factory.name;
+      }
+      
+      [Bindable(event="factory_set")]
+      public function get fieldDescription():String {
+          return _factory == null ? "" : _factory.description;
+      }
+      
+      [Bindable(event="relations_changed")]
+      public function get matchedRelations():Array {
+          if (_entity == null)
+              return [];
+          
+          var relationTags:Object = _factory.relationTags;
+          var matched:Array = [];
+          for each(var relation:Relation in _entity.parentRelations) {
+              var addable:Boolean = true;
+              for ( var k:String in relationTags ) {
+                  var relVal:String = relation.getTag(k);
+                  if ( relVal != relationTags[k] )
+                      addable = false;
+              }
+              if (addable)
+                  matched.push(relation);
+          }
+          return matched;
+      }
+      
+      public function addMember(relation:Relation, role:String):void {
+          if (_entity != null && !_entity.hasParent(relation))
+              relation.appendMember(new RelationMember(_entity, role));
+      }
+
+      public function set factory(factory:RelationMemberEditorFactory):void {
+          _factory = factory;
+          dispatchEvent(new Event("factory_set"));
+      }
+      
+      public function set entity(entity:Entity):void {
+          _entity = entity;
+          
+          // TODO: we need to listen for add/removal and relation tag changes
+          dispatchEvent(new Event("relations_changed"));
+      }
+      
+      private function relationsChanged(event:TagEvent):void {
+          dispatchEvent(new Event("relations_changed"));
+      }
+
+    }
+
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/RelationMemberEditorFactory.as b/net/systemeD/potlatch2/mapfeatures/editors/RelationMemberEditorFactory.as
new file mode 100644 (file)
index 0000000..86cd9aa
--- /dev/null
@@ -0,0 +1,53 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import flash.display.*;
+
+       public class RelationMemberEditorFactory extends EditorFactory {
+           private var _relationTags:Object;
+        
+        public function RelationMemberEditorFactory(inputXML:XML) {
+            super(inputXML);
+            _relationTags = {};
+            for each(var match:XML in inputXML.match) {
+                _relationTags[match.@k] = match.@v;
+            }
+        }
+        
+        public function get relationTags():Object {
+            return _relationTags;
+        }
+        
+        override public function areTagsMatching(entity:Entity):Boolean {
+            var parentRelations:Array = entity.parentRelations;
+            if ( parentRelations.length == 0 )
+                return false;
+                
+            // get relations for the entity
+            for each(var relation:Relation in parentRelations) {
+                for ( var k:String in _relationTags ) {
+                    var relVal:String = relation.getTag(k);
+                    if ( relVal != _relationTags[k] )
+                        return false;
+                }
+            }
+            // all must match
+            return true;
+        }
+        
+        override public function createEditorInstance(entity:Entity):DisplayObject {
+            var editor:RelationMemberEditor = createRelationMemberEditor();
+            editor.factory = this;
+            editor.entity = entity;
+            return editor;
+        }
+        
+        protected function createRelationMemberEditor():RelationMemberEditor {
+            return null;
+        }
+    }
+
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/RouteEditor.mxml b/net/systemeD/potlatch2/mapfeatures/editors/RouteEditor.mxml
new file mode 100644 (file)
index 0000000..9abe7e8
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<edit:RelationMemberEditor
+       xmlns:mx="http://www.adobe.com/2006/mxml" 
+       xmlns:edit="net.systemeD.potlatch2.mapfeatures.editors.*"
+       xmlns:flexlib="flexlib.controls.*"
+       verticalGap="0"
+       width="100%"
+       toolTip="{fieldDescription}"
+       initialize="addRoutes()">
+
+  <mx:Label text="{fieldName}:"/>
+  <mx:VBox verticalGap="0" width="100%" id="routeIcons"/>
+  <mx:LinkButton label="Add to route"/>
+
+  <mx:Script><![CDATA[
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.*;
+    import mx.managers.PopUpManager;
+    import mx.core.*;
+   
+    public function addRoutes():void {
+        for each(var relation:Relation in matchedRelations) {
+            var icon:RouteIcon = new RouteIcon();
+            icon.setRoute(relation, RouteEditorFactory(_factory).icon);
+            
+            icon.addEventListener(MouseEvent.CLICK, function(event:Event):void {
+                trace(relation+" is clicked");
+                var panel:RelationEditorPanel = RelationEditorPanel(
+                    PopUpManager.createPopUp(Application(Application.application), RelationEditorPanel, true));
+                panel.setRelation(relation);
+                PopUpManager.centerPopUp(panel);
+            });
+            routeIcons.addChild(icon);
+        }
+    }
+    
+  ]]></mx:Script>
+</edit:RelationMemberEditor>
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/RouteEditorFactory.as b/net/systemeD/potlatch2/mapfeatures/editors/RouteEditorFactory.as
new file mode 100644 (file)
index 0000000..5b7b5a3
--- /dev/null
@@ -0,0 +1,26 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import flash.display.*;
+
+       public class RouteEditorFactory extends RelationMemberEditorFactory {
+        private var _icon:XMLList;
+        
+        public function RouteEditorFactory(inputXML:XML) {
+            super(inputXML);
+            _icon = inputXML.icon;
+        }
+        
+        override protected function createRelationMemberEditor():RelationMemberEditor {
+            return new RouteEditor();
+        }
+        
+        public function get icon():XMLList {
+            return _icon;
+        }
+    }
+
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/RouteIcon.mxml b/net/systemeD/potlatch2/mapfeatures/editors/RouteIcon.mxml
new file mode 100644 (file)
index 0000000..dba2041
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<mx:HBox
+       xmlns:mx="http://www.adobe.com/2006/mxml" 
+       xmlns:edit="net.systemeD.potlatch2.mapfeatures.editors.*"
+       xmlns:flexlib="flexlib.controls.*"
+       borderStyle="inset" verticalAlign="middle" width="100%" paddingLeft="3"
+       backgroundColor="{bg}" color="{fg}" backgroundAlpha="1.0">
+       
+       <mx:Image source="{iconImage}"/>
+    <mx:Text condenseWhite="true" width="100%" htmlText="{iconHTML}" selectable="false"/>
+    
+    <mx:Script><![CDATA[
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.halcyon.styleparser.MapCSS;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import flash.events.*;
+
+    private var _iconImage:String = null;
+    private var _iconHTML:String = "";
+    private var _bg:String = "white";
+    private var _fg:String = "black";
+         
+    public function setRoute(relation:Relation, icon:XMLList):void {   
+        _iconHTML = Feature.makeHTMLIcon(icon, relation);
+
+        if ( icon.length() > 0 ) {
+            if (icon[0].hasOwnProperty("@image"))
+                _iconImage = icon[0].@image;
+            if (icon[0].hasOwnProperty("@foreground"))
+                _fg = icon[0].@foreground;
+            if (icon[0].hasOwnProperty("@background"))
+                _bg = icon[0].@background;
+        }    
+
+        dispatchEvent(new Event("route_changed"));
+    }
+    
+    [Bindable(event="route_changed")]
+    private function get iconImage():String {
+        return _iconImage;
+    }
+    
+    [Bindable(event="route_changed")]
+    private function get iconHTML():String {
+        return _iconHTML;
+    }
+
+    [Bindable(event="route_changed")]
+    private function get fg():uint {
+        return MapCSS.parseCSSColor(_fg);
+    }
+
+    [Bindable(event="route_changed")]
+    private function get bg():uint {
+        return MapCSS.parseCSSColor(_bg);
+    }
+    ]]></mx:Script>
+</mx:HBox>
+
diff --git a/resources/features/cycle__lcn.png b/resources/features/cycle__lcn.png
new file mode 100644 (file)
index 0000000..b46ab05
Binary files /dev/null and b/resources/features/cycle__lcn.png differ
diff --git a/resources/features/cycle__ncn.png b/resources/features/cycle__ncn.png
new file mode 100644 (file)
index 0000000..49456c4
Binary files /dev/null and b/resources/features/cycle__ncn.png differ
diff --git a/resources/features/cycle__rcn.png b/resources/features/cycle__rcn.png
new file mode 100644 (file)
index 0000000..ed79223
Binary files /dev/null and b/resources/features/cycle__rcn.png differ
index a3ab488..79da68b 100644 (file)
@@ -35,6 +35,7 @@
     <inputSet ref="roadNames"/>
     <inputSet ref="roadRestrictions"/>
     <inputSet ref="roadPhysical"/>
+    <inputSet ref="cycle"/>
   </inputSet>
   
   <inputSet id="roadNames">
     <inputSet ref="naptan" />
   </inputSet>
   
+  <inputSet id="cycle">
+    <input type="route" name="National Cycle Route" description="National cycle route" category="Cycle" priority="normal">
+       <match k="type" v="route"/>
+       <match k="network" v="ncn"/>
+       <icon image="features/cycle__ncn.png" background="red" foreground="white">
+         <font size="14pt"><b>${ref}</b></font><br />
+         <font size="12pt">${name}</font>
+       </icon>
+    </input>
+    <input type="route" name="Regional Cycle Route" description="Regional cycle route" category="Cycle" priority="low">
+       <match k="type" v="route"/>
+       <match k="network" v="rcn"/>
+       <icon image="features/cycle__rcn.png" background="cyan" foreground="white">
+         <font size="14pt"><b>${ref}</b></font><br />
+         <font size="12pt">${name}</font>
+       </icon>
+    </input>
+    <input type="route" name="Local Cycle Route" description="Local cycle route" category="Cycle" priority="lowest">
+       <match k="type" v="route"/>
+       <match k="network" v="lcn"/>
+       <icon image="features/cycle__lcn.png" background="blue" foreground="white">
+         <font size="14pt"><b>${ref}</b></font><br />
+         <font size="12pt">${name}</font>
+       </icon>
+    </input>
+  </inputSet>
+  
+  <inputSet id="cycle-route">
+    <inputSet ref="roadNames"/>
+    <inputSet ref="roadRefs"/>
+  </inputSet>
+  
+  
   <feature name="Motorway">
     <category>roads</category>
     <icon image="features/highway__motorway.png">
     <icon image="features/highway__residential.png">
       <font size="14pt"><b>${name}</b></font><br/>
       <font size="8pt">${postal_code}</font><br/>
-      <font size="8pt"><i>A residential road is one surrounded by houses, and that isn't a higher classification.</i></font>
     </icon>
 
     <line/>
     <tag k="highway" v="bus_stop"/>
     <inputSet ref="buses" />
   </feature>
+
+  <!-- cycle stuff -->
+  <feature name="National Cycle Network">
+    <category>paths</category>
+    <icon image="features/cycle__ncn.png" background="red" foreground="white">
+      <font size="14pt"><b>${ref}</b></font><br />
+      <font size="12pt">${name}</font>
+    </icon>
+
+    <relation/>
+    <tag k="type" v="route"/>
+    <tag k="network" v="ncn"/>
+      
+    <inputSet ref="cycle-route"/>
+  </feature>
+  
+  <feature name="Regional Cycle Network">
+    <category>paths</category>
+    <icon image="features/cycle__rcn.png" background="cyan" foreground="white">
+      <font size="14pt"><b>${ref}</b></font><br />
+      <font size="12pt">${name}</font>
+    </icon>
+
+    <relation/>
+    <tag k="type" v="route"/>
+    <tag k="network" v="rcn"/>
+      
+    <inputSet ref="cycle-route"/>
+  </feature>
+
+  <feature name="Local Cycle Network">
+    <category>paths</category>
+    <icon image="features/cycle__lcn.png" background="blue" foreground="white">
+      <font size="14pt"><b>${ref}</b></font><br />
+      <font size="12pt">${name}</font>
+    </icon>
+
+    <relation/>
+    <tag k="type" v="route"/>
+    <tag k="network" v="lcn"/>
+      
+    <inputSet ref="cycle-route"/>
+  </feature>
+
 </mapFeatures>