MapCSS in progress. Vast amounts still to do, but the meat of it is here. POIs are...
authorRichard Fairhurst <richard@systemed.net>
Thu, 3 Sep 2009 22:27:37 +0000 (22:27 +0000)
committerRichard Fairhurst <richard@systemed.net>
Thu, 3 Sep 2009 22:27:37 +0000 (22:27 +0000)
20 files changed:
halcyon.mxml
halcyon_viewer.as
net/systemeD/halcyon/Map.as
net/systemeD/halcyon/POI.as
net/systemeD/halcyon/WayUI.as
net/systemeD/halcyon/styleparser/Condition.as
net/systemeD/halcyon/styleparser/InstructionStyle.as [new file with mode: 0755]
net/systemeD/halcyon/styleparser/MapCSS.as [new file with mode: 0755]
net/systemeD/halcyon/styleparser/PointRule.as [deleted file]
net/systemeD/halcyon/styleparser/PointStyle.as
net/systemeD/halcyon/styleparser/Rule.as
net/systemeD/halcyon/styleparser/RuleSet.as
net/systemeD/halcyon/styleparser/ShapeRule.as [deleted file]
net/systemeD/halcyon/styleparser/ShapeStyle.as
net/systemeD/halcyon/styleparser/ShieldStyle.as
net/systemeD/halcyon/styleparser/Style.as [new file with mode: 0755]
net/systemeD/halcyon/styleparser/StyleChooser.as [new file with mode: 0755]
net/systemeD/halcyon/styleparser/StyleList.as [new file with mode: 0755]
net/systemeD/halcyon/styleparser/TextStyle.as
resources/test.css [new file with mode: 0644]

index 513d6fc..ca19e63 100755 (executable)
                        _root.addChild(t);
                        Globals.vars.debug=t;
             t.visible = loaderInfo.parameters["show_debug"] == 'true';
+                       Globals.vars.root=theMap;       // just for the addDebug function
 
             var controller:EditController = new EditController(theMap, tagViewer);
             controller.setActive();
index df1a13a..74f0aa1 100755 (executable)
@@ -8,6 +8,7 @@ package {
        import flash.events.MouseEvent;
        import flash.display.*;
        import flash.text.TextField;
+       import bustin.dev.Inspector;
 
        public class halcyon_viewer extends Sprite {
 
@@ -27,16 +28,17 @@ package {
                        t.multiline=true;
                        addChild(t);
                        Globals.vars.debug=t;
-                       t.visible = false;
+                       t.visible = true;
 
                        theMap = new Map(this.loaderInfo.parameters);
             theMap.updateSize(stage.stageWidth, stage.stageHeight);
                        addChild(theMap);
                        Globals.vars.root=theMap;
 
-                       stage.addEventListener(MouseEvent.MOUSE_UP, theMap.mouseUpHandler);
-                       stage.addEventListener(MouseEvent.MOUSE_MOVE, theMap.mouseMoveHandler);
-                       stage.addEventListener(MouseEvent.MOUSE_DOWN, theMap.mouseDownHandler);
+//                     stage.addEventListener(MouseEvent.MOUSE_UP, theMap.mouseUpHandler);
+//                     stage.addEventListener(MouseEvent.MOUSE_MOVE, theMap.mouseMoveHandler);
+//                     stage.addEventListener(MouseEvent.MOUSE_DOWN, theMap.mouseDownHandler);
+                       Inspector.getInstance().init(stage);
 
                        var z1:Sprite=new Sprite();
                        z1.graphics.beginFill(0x0000FF); z1.graphics.drawRoundRect(0,0,20,20,5); z1.graphics.endFill();
index 45344c3..43ce446 100755 (executable)
@@ -28,7 +28,7 @@ package net.systemeD.halcyon {
                public const MINSCALE:uint=13;                                  // don't zoom out past this
                public const MAXSCALE:uint=19;                                  // don't zoom in past this
 
-               public var ruleset:RuleSet=new RuleSet(redrawPOIs);     // rules
+               public var ruleset:RuleSet;                                             // rules
                
                public var ways:Object=new Object();                    // geodata
                public var nodes:Object=new Object();                   //  |
@@ -69,6 +69,7 @@ package net.systemeD.halcyon {
                public var initparams:Object;                                   // object containing 
 
                public var backdrop:Object;                                             // reference to backdrop sprite
+               public var showall:Boolean=true;                                // show all objects, even if unstyled?
                
                public var connection:Connection;                               // server connection
 
@@ -143,10 +144,8 @@ package net.systemeD.halcyon {
                // Initialise map at a given lat/lon
 
         public function init(startlat:Number,startlon:Number,startscale:uint,style:String):void {
-
-                       ruleset.load(style);
-//                     rules.initExample();            // initialise dummy rules
-
+                       ruleset=new RuleSet(this,redrawPOIs);
+                       ruleset.loadFromCSS("test.css?"+Math.random());         // ** test for MapCSS
                        //updateSize();
 
                        scale=startscale;
index ad0b40c..2a39c71 100644 (file)
@@ -33,32 +33,39 @@ package net.systemeD.halcyon {
                
                public function redraw():void {
                        var tags:Object = node.getTagsCopy();
-                       var styles:Array=map.ruleset.getStyles(true,tags,map.scale);
+                       var sl:StyleList=map.ruleset.getStyles(this.node,map.scale);
                        var r:Boolean=false;    // ** rendered
-                       for each (var s:* in styles) {
-                               if ((s is PointStyle) && s.icon && s.icon!="") {
+                       for (var sublayer:uint=0; sublayer<10; sublayer++) {
+
+                               if (sl.pointStyles[sublayer]) {
+                                       var s:PointStyle=sl.pointStyles[sublayer];
+//                                     if ((s is PointStyle) && s.icon && s.icon!="") 
                                        r=true;
-                                       if (s.icon!=iconname) {
+                                       if (s.icon_image!=iconname) {
                                                // 'load' icon (actually just from library)
-                                               if (map.ruleset.images[s.icon]) {
+                                               if (map.ruleset.images[s.icon_image]) {
                                                        var loader:Loader = new Loader();
                                                        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadedIcon);
-                                                       loader.loadBytes(map.ruleset.images[s.icon]);
-                                                       iconname=s.icon;
+                                                       loader.loadBytes(map.ruleset.images[s.icon_image]);
+                                                       iconname=s.icon_image;
                                                }
                                        } else {
                                                // already loaded, so just reposition
                                                updatePosition();
-                                               iconname=s.icon;
+                                               iconname=s.icon_image;
                                        }
-                               } else if ((s is TextStyle) && s.tag && tags[s.tag]) {
+                               }
+
+                               if (sl.textStyles[sublayer]) {
+                                       var t:TextStyle=sl.textStyles[sublayer];
+//                                     if ((s is TextStyle) && s.tag && tags[s.tag])
                                        // create name sprite
                                        if (!name) {
                                                name=new Sprite();
                                                var c:DisplayObject=map.getChildAt(12);
                                                Sprite(c).addChild(name);
                                        }
-                                       s.writeNameLabel(name,tags[s.tag],map.lon2coord(node.lon),map.latp2coord(node.latp));
+                                       t.writeNameLabel(name,tags[t.text],map.lon2coord(node.lon),map.latp2coord(node.latp));
                                }
                        }
                        if (!r && iconname!='') {
index 8dae6c8..3a3c530 100755 (executable)
@@ -98,85 +98,107 @@ package net.systemeD.halcyon {
                // Redraw
 
                public function redraw():void {
+            // Copy tags object, and add states
             var tags:Object = way.getTagsCopy();
-            // add states to the tags -- we should probably treat these
-            // more explicitly, but if Richard is still playing with styles
-            // this will work for now
-            for ( var stateKey:String in stateClasses ) {
-                tags["__state__"+stateKey] = stateKey;
+            for (var stateKey:String in stateClasses) {
+                tags[":"+stateKey] = stateKey;
             }
 
-                       // remove all currently existing sprites
+                       // Remove all currently existing sprites
                        while (sprites.length>0) {
                                var d:DisplayObject=sprites.pop(); d.parent.removeChild(d);
                        }
 
-                       // which layer?
+                       // Which layer?
                        layer=5;
                        if ( tags['layer'] )
                 layer=Math.min(Math.max(tags['layer']+5,-5),5)+5;
 
-                       // set style
-                       var styles:Array=map.ruleset.getStyles(false, tags, map.scale);
-                       for each (var s:* in styles) {
-
-                               if (s is ShapeStyle) {
+                       // Iterate through each sublayer, drawing any styles on that layer
+                       var sl:StyleList=map.ruleset.getStyles(this.way, tags);
+                       var drawn:Boolean=false;
+                       for (var sublayer:uint=0; sublayer<11; sublayer++) {
+                               if (sl.shapeStyles[sublayer]) {
+Globals.vars.root.addDebug("adding shapestyle on "+sublayer); 
+                                       var s:ShapeStyle=sl.shapeStyles[sublayer];
                                        var stroke:Shape, fill:Shape, roadname:Sprite, f:Graphics, g:Graphics;
-                                       var doStroke:Boolean=false, doDashed:Boolean=false;
-                                       var doFill:Boolean=false, fill_colour:uint, fill_opacity:Number;
-                                       var doCasing:Boolean=false, doDashedCasing:Boolean=false;
 
                                        // Set stroke style
-                                       if (s.isStroked)  {
-                                               stroke=new Shape(); addToLayer(stroke,1,s.sublayer); g=stroke.graphics;
+                                       if (s.width)  {
+                                               stroke=new Shape(); addToLayer(stroke,1,sublayer); g=stroke.graphics;
                                g.moveTo(map.lon2coord(way.getNode(0).lon), map.latp2coord(way.getNode(0).latp));
-                                               g.lineStyle(s.stroke_width, s.stroke_colour, s.stroke_opacity/100,
-                                                                       false, "normal", s.stroke_linecap, s.stroke_linejoin);
+                                               g.lineStyle(s.width,
+                                                                       s.color ? s.color : 0,
+                                                                       s.opacity ? s.opacity : 1,
+                                                                       false, "normal",
+                                                                       s.linecap  ? s.linecap : "none",
+                                                                       s.linejoin ? s.linejoin : "round");
                                        }
 
                                        // Set fill and casing style
-                                       if (s.isFilled || s.isCased) {
+                                       if (s.fill_color || s.casing_width) {
                                                fill=new Shape(); addToLayer(fill,0); f=fill.graphics;
                                f.moveTo(map.lon2coord(way.getNode(0).lon), map.latp2coord(way.getNode(0).latp));
-                                               if (s.isCased)  { f.lineStyle(s.casing_width, s.casing_colour, s.casing_opacity/100,
-                                                                                 false, "normal", s.stroke_linecap, s.stroke_linejoin); }
-                                               if (s.isFilled) { f.beginFill(s.fill_colour,s.fill_opacity/100); }
+                                               if (s.casing_width)  {
+                                                       f.lineStyle(s.casing_width,
+                                                                               s.casing_color   ? s.casing_color : 0,
+                                                                               s.casing_opacity ? s.casing_opacity : 1,
+                                                                               false, "normal",
+                                                                               s.linecap  ? s.linecap : "none",
+                                                                               s.linejoin ? s.linejoin : "round");
+                                               }
+                                               if (s.fill_color) {
+                                                       f.beginFill(s.fill_color,
+                                                                               s.fill_opacity ? s.fill_opacity : 1);
+                                               }
                                        }
 
                                        // Draw stroke
-                                       if (s.stroke_dashArray.length>0) { dashedLine(g,s.stroke_dashArray); }
-                                                          else if (s.isStroked) { solidLine(g); }
+                                       if (s.dashes && s.dashes.length>0) {
+                                               dashedLine(g,s.dashes); drawn=true;
+                                       } else if (s.width) { 
+                                               solidLine(g); drawn=true;
+                                       }
                        
                                        // Draw fill and casing
-                                       if (s.casing_dashArray.length>0) { dashedLine(f,s.casing_dashArray); f.lineStyle(); }
-                                       if (s.isFilled)                                  { f.beginFill(s.fill_colour,s.fill_opacity/100); 
-                                                                                                          solidLine(f); f.endFill(); }
-                                       else if (s.isCased && s.casing_dashArray.length==0) { solidLine(f); }
+                                       if (s.casing_dashes && s.casing_dashes.length>0) {
+                                               dashedLine(f,s.casing_dashes); f.lineStyle(); drawn=true;
+                                       }
+                                       if (s.fill_color) {
+                                               f.beginFill(s.fill_color,s.fill_opacity); 
+                                               solidLine(f); f.endFill(); drawn=true;
+                                       } else if (s.casing_width && (!s.casing_dashes || s.casing_dashes.length==0)) {
+                                               solidLine(f); drawn=true;
+                                       }
 
 
-                               } else if (s is TextStyle && s.tag && tags[s.tag]) {
+                               }
+                               
+                               if (sl.textStyles[sublayer]) {
+                                       var t:TextStyle=sl.textStyles[sublayer];
                                        roadname=new Sprite(); addToLayer(roadname,2);
-                                       nameformat = s.getTextFormat();
-                                       var a:String=tags[s.tag]; if (s.font_caps) { a=a.toUpperCase(); }
-                                       if (s.isLine) {
-                                               writeNameOnPath(roadname,a,s.text_offset ? s.text_offset : 0);
-                                               if (s.pullout_radius>0) { roadname.filters=s.getPulloutFilter(); }
+                                       nameformat = t.getTextFormat();
+                                       var a:String=tags[t.text]; if (t.font_caps) { a=a.toUpperCase(); }
+                                       if (t.text_onpath) {
+                                               writeNameOnPath(roadname,a,t.text_offset ? t.text_offset : 0);
+                                               if (t.text_halo_radius>0) { roadname.filters=t.getHaloFilter(); }
                                        } else if (centroid_x) {
-                                               s.writeNameLabel(roadname,tags[s.tag],centroid_x,centroid_y);
+                                               t.writeNameLabel(roadname,tags[t.text],centroid_x,centroid_y);
                                        }
 
 
-                               } else if (s is ShieldStyle) {
-                                       // ** to do
                                }
+                               
+                               // ** ShieldStyle to do
                        }
 
-            if ( styles.length == 0 ) {
-                // there's no styles... so add a thin trace
+                       // No styles, so add a thin trace
+            if (!drawn && map.showall) {
                 var def:Sprite = new Sprite();
                 def.graphics.lineStyle(0.5, 0x808080, 1, false, "normal");
                 solidLine(def.graphics);
                 addToLayer(def, 1);
+                               drawn=true;
             }
             
             if ( stateClasses["showNodes"] != null ) {
@@ -185,6 +207,8 @@ package net.systemeD.halcyon {
                 addToLayer(nodes, 2);
             }
 
+                       if (!drawn) { return; }
+                       
             // create a generic "way" hitzone sprite
             hitzone = new Sprite();
             hitzone.graphics.lineStyle(4, 0x000000, 1, false, "normal", CapsStyle.ROUND, JointStyle.ROUND);
index e19e532..6ac9936 100644 (file)
@@ -22,6 +22,7 @@ package net.systemeD.halcyon.styleparser {
                public function test(tags:Object):Boolean {
                        switch (type) {
                                case 'eq':              return (tags[params[0]]==params[1]); break;
+                               case 'ne':              return (tags[params[0]]!=params[1]); break;
                                case 'regex':   var r:RegExp=new RegExp(params[1],"i");
                                                                return (r.test(tags[params[0]])); break;
                                case 'true':    return (tags[params[0]]=='true' || tags[params[0]]=='yes' || tags[params[0]]=='1'); break;
@@ -35,6 +36,12 @@ package net.systemeD.halcyon.styleparser {
                        }
                        return false;
                }
+
+        public function toString():String {
+            return "Condition("+type+":"+params+")";
+        }
+
+
        }
 
 }
diff --git a/net/systemeD/halcyon/styleparser/InstructionStyle.as b/net/systemeD/halcyon/styleparser/InstructionStyle.as
new file mode 100755 (executable)
index 0000000..d73b43f
--- /dev/null
@@ -0,0 +1,19 @@
+package net.systemeD.halcyon.styleparser {
+
+       import net.systemeD.halcyon.Globals;
+
+       public class InstructionStyle extends Style {
+
+               public var set_tags:Object;
+               public var breaker:Boolean=false;
+
+               public function addSetTag(k:String,v:*):void {
+                       edited=true;
+                       if (!set_tags) { set_tags=new Object(); }
+                       set_tags[k]=v;
+                       Globals.vars.root.addDebug("set "+k+" to "+v);
+               }
+
+       }
+
+}
diff --git a/net/systemeD/halcyon/styleparser/MapCSS.as b/net/systemeD/halcyon/styleparser/MapCSS.as
new file mode 100755 (executable)
index 0000000..5eb6da5
--- /dev/null
@@ -0,0 +1,388 @@
+package net.systemeD.halcyon.styleparser {
+
+       /*
+               MapCSS parser
+               creates a RuleSet from a .mapcss file
+
+       */
+
+       import flash.events.*;
+       import flash.net.*;
+       import net.systemeD.halcyon.Globals;
+       import net.systemeD.halcyon.Map;
+
+       // ** also needs to support @import rules
+
+       public class MapCSS {
+               private var map:Map;
+               private var choosers:Array;
+
+               private static const WHITESPACE:RegExp  =/^ \s+ /sx;
+               private static const COMMENT:RegExp             =/^ \/* .+? *\/ \s* /sx;        /* */
+               private static const CLASS:RegExp               =/^ ([\.:]\w+) \s* /sx;
+               private static const ZOOM:RegExp                =/^ \| \s* z([\d\-]+) \s* /isx;
+               private static const GROUP:RegExp               =/^ , \s* /isx;
+               private static const CONDITION:RegExp   =/^ \[(.+?)\] \s* /sx;
+               private static const OBJECT:RegExp              =/^ (\w+) \s* /sx;
+               private static const DECLARATION:RegExp =/^ \{(.+?)\} \s* /sx;
+               private static const UNKNOWN:RegExp             =/^ (\S+) \s* /sx;
+
+               private static const ZOOM_MINMAX:RegExp =/^ (\d+)\-(\d+) $/sx;
+               private static const ZOOM_MIN:RegExp    =/^ (\d+)\-      $/sx;
+               private static const ZOOM_MAX:RegExp    =/^      \-(\d+) $/sx;
+               private static const ZOOM_SINGLE:RegExp =/^        (\d+) $/sx;
+
+               private static const CONDITION_TRUE:RegExp      =/^ \s* (\w+) \s* = \s* yes \s*  $/isx;
+               private static const CONDITION_FALSE:RegExp     =/^ \s* (\w+) \s* = \s* no  \s*  $/isx;
+               private static const CONDITION_SET:RegExp       =/^ \s* (\w+) \s* $/sx;
+               private static const CONDITION_UNSET:RegExp     =/^ \s* !(\w+) \s* $/sx;
+               private static const CONDITION_EQ:RegExp        =/^ \s* (\w+) \s* =  \s* (.+) \s* $/sx;
+               private static const CONDITION_NE:RegExp        =/^ \s* (\w+) \s* != \s* (.+) \s* $/sx;
+               private static const CONDITION_GT:RegExp        =/^ \s* (\w+) \s* >  \s* (.+) \s* $/sx;
+               private static const CONDITION_GE:RegExp        =/^ \s* (\w+) \s* >= \s* (.+) \s* $/sx;
+               private static const CONDITION_LT:RegExp        =/^ \s* (\w+) \s* <  \s* (.+) \s* $/sx;
+               private static const CONDITION_LE:RegExp        =/^ \s* (\w+) \s* <= \s* (.+) \s* $/sx;
+               private static const CONDITION_REGEX:RegExp     =/^ \s* (\w+) \s* =~\/ \s* (.+) \/ \s* $/sx;
+
+               private static const ASSIGNMENT:RegExp          =/^ \s* (\S+) \s* \: \s* (.+?) \s* $/sx;
+               private static const SET_TAG:RegExp                     =/^ \s* set \s+(\S+)\s* = \s* (.+?) \s* $/sx;
+               private static const SET_TAG_TRUE:RegExp        =/^ \s* set \s+(\S+)\s* $/sx;
+               private static const EXIT:RegExp                        =/^ \s* exit \s* $/isx;
+
+               private static const oZOOM:uint=2;
+               private static const oGROUP:uint=3;
+               private static const oCONDITION:uint=4;
+               private static const oOBJECT:uint=5;
+               private static const oDECLARATION:uint=6;
+
+               private static const DASH:RegExp=/\-/g;
+               private static const COLOR:RegExp=/color$/;
+               private static const BOLD:RegExp=/^bold$/i;
+               private static const ITALIC:RegExp=/^italic|oblique$/i;
+               private static const CAPS:RegExp=/^uppercase$/i;
+               private static const LINE:RegExp=/^line$/i;
+
+               private static const CSSCOLORS:Object = {
+                       aliceblue:0xf0f8ff,
+                       antiquewhite:0xfaebd7,
+                       aqua:0x00ffff,
+                       aquamarine:0x7fffd4,
+                       azure:0xf0ffff,
+                       beige:0xf5f5dc,
+                       bisque:0xffe4c4,
+                       black:0x000000,
+                       blanchedalmond:0xffebcd,
+                       blue:0x0000ff,
+                       blueviolet:0x8a2be2,
+                       brown:0xa52a2a,
+                       burlywood:0xdeb887,
+                       cadetblue:0x5f9ea0,
+                       chartreuse:0x7fff00,
+                       chocolate:0xd2691e,
+                       coral:0xff7f50,
+                       cornflowerblue:0x6495ed,
+                       cornsilk:0xfff8dc,
+                       crimson:0xdc143c,
+                       cyan:0x00ffff,
+                       darkblue:0x00008b,
+                       darkcyan:0x008b8b,
+                       darkgoldenrod:0xb8860b,
+                       darkgray:0xa9a9a9,
+                       darkgreen:0x006400,
+                       darkkhaki:0xbdb76b,
+                       darkmagenta:0x8b008b,
+                       darkolivegreen:0x556b2f,
+                       darkorange:0xff8c00,
+                       darkorchid:0x9932cc,
+                       darkred:0x8b0000,
+                       darksalmon:0xe9967a,
+                       darkseagreen:0x8fbc8f,
+                       darkslateblue:0x483d8b,
+                       darkslategray:0x2f4f4f,
+                       darkturquoise:0x00ced1,
+                       darkviolet:0x9400d3,
+                       deeppink:0xff1493,
+                       deepskyblue:0x00bfff,
+                       dimgray:0x696969,
+                       dodgerblue:0x1e90ff,
+                       firebrick:0xb22222,
+                       floralwhite:0xfffaf0,
+                       forestgreen:0x228b22,
+                       fuchsia:0xff00ff,
+                       gainsboro:0xdcdcdc,
+                       ghostwhite:0xf8f8ff,
+                       gold:0xffd700,
+                       goldenrod:0xdaa520,
+                       gray:0x808080,
+                       green:0x008000,
+                       greenyellow:0xadff2f,
+                       honeydew:0xf0fff0,
+                       hotpink:0xff69b4,
+                       indianred :0xcd5c5c,
+                       indigo :0x4b0082,
+                       ivory:0xfffff0,
+                       khaki:0xf0e68c,
+                       lavender:0xe6e6fa,
+                       lavenderblush:0xfff0f5,
+                       lawngreen:0x7cfc00,
+                       lemonchiffon:0xfffacd,
+                       lightblue:0xadd8e6,
+                       lightcoral:0xf08080,
+                       lightcyan:0xe0ffff,
+                       lightgoldenrodyellow:0xfafad2,
+                       lightgrey:0xd3d3d3,
+                       lightgreen:0x90ee90,
+                       lightpink:0xffb6c1,
+                       lightsalmon:0xffa07a,
+                       lightseagreen:0x20b2aa,
+                       lightskyblue:0x87cefa,
+                       lightslategray:0x778899,
+                       lightsteelblue:0xb0c4de,
+                       lightyellow:0xffffe0,
+                       lime:0x00ff00,
+                       limegreen:0x32cd32,
+                       linen:0xfaf0e6,
+                       magenta:0xff00ff,
+                       maroon:0x800000,
+                       mediumaquamarine:0x66cdaa,
+                       mediumblue:0x0000cd,
+                       mediumorchid:0xba55d3,
+                       mediumpurple:0x9370d8,
+                       mediumseagreen:0x3cb371,
+                       mediumslateblue:0x7b68ee,
+                       mediumspringgreen:0x00fa9a,
+                       mediumturquoise:0x48d1cc,
+                       mediumvioletred:0xc71585,
+                       midnightblue:0x191970,
+                       mintcream:0xf5fffa,
+                       mistyrose:0xffe4e1,
+                       moccasin:0xffe4b5,
+                       navajowhite:0xffdead,
+                       navy:0x000080,
+                       oldlace:0xfdf5e6,
+                       olive:0x808000,
+                       olivedrab:0x6b8e23,
+                       orange:0xffa500,
+                       orangered:0xff4500,
+                       orchid:0xda70d6,
+                       palegoldenrod:0xeee8aa,
+                       palegreen:0x98fb98,
+                       paleturquoise:0xafeeee,
+                       palevioletred:0xd87093,
+                       papayawhip:0xffefd5,
+                       peachpuff:0xffdab9,
+                       peru:0xcd853f,
+                       pink:0xffc0cb,
+                       plum:0xdda0dd,
+                       powderblue:0xb0e0e6,
+                       purple:0x800080,
+                       red:0xff0000,
+                       rosybrown:0xbc8f8f,
+                       royalblue:0x4169e1,
+                       saddlebrown:0x8b4513,
+                       salmon:0xfa8072,
+                       sandybrown:0xf4a460,
+                       seagreen:0x2e8b57,
+                       seashell:0xfff5ee,
+                       sienna:0xa0522d,
+                       silver:0xc0c0c0,
+                       skyblue:0x87ceeb,
+                       slateblue:0x6a5acd,
+                       slategray:0x708090,
+                       snow:0xfffafa,
+                       springgreen:0x00ff7f,
+                       steelblue:0x4682b4,
+                       tan:0xd2b48c,
+                       teal:0x008080,
+                       thistle:0xd8bfd8,
+                       tomato:0xff6347,
+                       turquoise:0x40e0d0,
+                       violet:0xee82ee,
+                       wheat:0xf5deb3,
+                       white:0xffffff,
+                       whitesmoke:0xf5f5f5,
+                       yellow:0xffff00,
+                       yellowgreen:0x9acd32 };
+
+
+               public function MapCSS(m:Map) {
+                       map=m;
+               }
+               
+               public function parse(css:String):Array {
+                       var previous:uint=0;                                    // what was the previous CSS word?
+                       var sc:StyleChooser=new StyleChooser(); // currently being assembled
+                       choosers=new Array();
+
+                       var o:Object=new Object();
+                       while (css.length>0) {
+
+                               // CSS comment
+                               if ((o=COMMENT.exec(css))) {
+                                       css=css.replace(COMMENT,'');
+
+                               // Whitespace (probably only at beginning of file)
+                               } else if ((o=WHITESPACE.exec(css))) {
+                                       css=css.replace(WHITESPACE,'');
+
+                               // Class - .motorway, .builtup, :hover
+                               } else if ((o=CLASS.exec(css))) {
+                                       if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
+
+                                       css=css.replace(CLASS,'');
+                                       sc.addCondition(new Condition('set',o[1]));
+                                       previous=oCONDITION;
+                                       Globals.vars.root.addDebug("class: "+o[1]);
+
+                               // Zoom
+                               } else if ((o=ZOOM.exec(css))) {
+                                       if (previous!=oOBJECT) { sc.newObject(); }
+
+                                       css=css.replace(ZOOM,'');
+                                       var z:Array=parseZoom(o[1]);
+                                       sc.addZoom(z[0],z[1]);
+                                       Globals.vars.root.addDebug("zoom: "+o[1]+"->"+parseZoom(o[1]));
+                                       previous=oZOOM;
+
+                               // Grouping - just a comma
+                               } else if ((o=GROUP.exec(css))) {
+                                       css=css.replace(GROUP,'');
+                                       sc.newGroup();
+                                       Globals.vars.root.addDebug("group");
+                                       previous=oGROUP;
+
+                               // Condition - [highway=primary]
+                               } else if ((o=CONDITION.exec(css))) {
+                                       if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
+                                       if (previous!=oOBJECT && previous!=oZOOM) { sc.newObject(); }
+
+                                       css=css.replace(CONDITION,'');
+                                       sc.addCondition(parseCondition(o[1]) as Condition);
+                                       Globals.vars.root.addDebug("condition: "+o[1]+'->'+parseCondition(o[1]));
+                                       previous=oCONDITION;
+
+                               // Object - way, node, relation
+                               } else if ((o=OBJECT.exec(css))) {
+                                       if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
+
+                                       css=css.replace(OBJECT,'');
+                                       sc.newObject(o[1]);
+                                       Globals.vars.root.addDebug("object: "+o[1]);
+                                       previous=oOBJECT;
+
+                               // Declaration - {...}
+                               } else if ((o=DECLARATION.exec(css))) {
+                                       css=css.replace(DECLARATION,'');
+                                       Globals.vars.root.addDebug("declaration: "+o[1]);
+                                       sc.addStyles(parseDeclaration(o[1]));
+                                       previous=oDECLARATION;
+                               
+                               // Unknown pattern
+                               } else if ((o=UNKNOWN.exec(css))) {
+                                       css=css.replace(UNKNOWN,'');
+                                       Globals.vars.root.addDebug("unknown: "+o[1]);
+                                       // ** do some debugging with o[1]
+
+                               } else {
+                                       Globals.vars.root.addDebug("choked on "+css);
+                                       return choosers;
+                               }
+                       }
+                       if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
+                       return choosers;
+               }
+               
+               private function saveChooser(sc:StyleChooser):void {
+Globals.vars.root.addDebug("+ saveChooser [rc="+sc.ruleChains[0][0]+"]");
+                       choosers.push(sc);
+               };
+
+               // Parse declaration string into list of styles
+
+               private function parseDeclaration(s:String):Array {
+                       Globals.vars.root.addDebug("entering parseDeclaration with "+s); 
+                       var styles:Array=[];
+                       var t:Object=new Object();
+                       var o:Object=new Object();
+                       var a:String;
+
+                       // Create styles\10
+                       var ss:ShapeStyle =new ShapeStyle() ;
+                       var ps:PointStyle =new PointStyle() ; 
+                       var ts:TextStyle  =new TextStyle()  ; 
+                       var hs:ShieldStyle=new ShieldStyle(); 
+                       var xs:InstructionStyle=new InstructionStyle(); 
+
+                       for each (a in s.split(';')) {
+                               if ((o=ASSIGNMENT.exec(a))) { t[o[1].replace(DASH,'_')]=o[2]; }
+                               else if ((o=SET_TAG.exec(a))) { xs.addSetTag(o[1],o[2]); }
+                               else if ((o=SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); }
+                               else if ((o=EXIT.exec(a))) { xs.setPropertyFromString('breaker',true); }
+                       }
+if (xs.edited) { Globals.vars.root.addDebug("xs.set_tags is *"+xs.set_tags+"*");  }
+
+                       // Find sublayer
+                       var sub:uint=5;
+                       if (t['z_index']) { sub=Number(t['z_index']); delete t['z_index']; }
+                       ss.sublayer=ps.sublayer=ts.sublayer=hs.sublayer=sub;
+                       xs.sublayer=10;
+
+                       // Munge special values
+                       if (t['font_weight']   ) { if (t['font_weight'].match(BOLD)   ) { t['font_bold']=true;   } else { t['font_bold']=false;   } }
+                       if (t['font_style']    ) { if (t['font_style'].match(ITALIC)  ) { t['font_italic']=true; } else { t['font_italic']=false; } }
+                       if (t['text_transform']) { if (t['text_transform'].match(CAPS)) { t['font_caps']=true;   } else { t['font_caps']=false;   } }
+                       if (t['text_position'] ) { if (t['text_position'].match(LINE) ) { t['text_onpath']=true; } else { t['text_onpath']=false; } }
+
+                       // ** Do compound settings (e.g. line: 5px dotted blue;)
+
+                       // Assign each property to the appropriate style
+                       for (a in t) {
+                               // Parse properties
+                               // ** also do units, e.g. px/pt
+                               // ** convert # colours to 0x numbers
+Globals.vars.root.addDebug("looking at property *"+a+"*"); 
+                               if (a.match(COLOR) && CSSCOLORS[t[a].toLowerCase()]) { t[a]=CSSCOLORS[t[a].toLowerCase()]; }
+                               
+                               // Set in styles
+                               if      (ss.hasOwnProperty(a)) { ss.setPropertyFromString(a,t[a]); Globals.vars.root.addDebug("added "+a+" to ShapeStyle"); }
+                               else if (ps.hasOwnProperty(a)) { ps.setPropertyFromString(a,t[a]); Globals.vars.root.addDebug("added "+a+" to PointStyle"); }
+                               else if (ts.hasOwnProperty(a)) { ts.setPropertyFromString(a,t[a]); Globals.vars.root.addDebug("added "+a+" to TextStyle"); }
+                               else if (hs.hasOwnProperty(a)) { hs.setPropertyFromString(a,t[a]); Globals.vars.root.addDebug("added "+a+" to ShieldStyle"); }
+                       }
+
+                       // Add each style to list
+                       if (ss.edited) { styles.push(ss); Globals.vars.root.addDebug("added ShapeStyle"); }
+                       if (ps.edited) { styles.push(ps); Globals.vars.root.addDebug("added PointStyle"); }
+                       if (ts.edited) { styles.push(ts); }
+                       if (hs.edited) { styles.push(hs); }
+                       if (xs.edited) { styles.push(xs); }
+                       return styles;
+               }
+               
+               private function parseZoom(s:String):Array {
+                       var o:Object=new Object();
+                       if ((o=ZOOM_MINMAX.exec(s))) { return [o[1],o[2]]; }
+                       else if ((o=ZOOM_MIN.exec(s))) { return [o[1],map.MAXSCALE]; }
+                       else if ((o=ZOOM_MAX.exec(s))) { return [map.MINSCALE,o[1]]; }
+                       else if ((o=ZOOM_SINGLE.exec(s))) { return [o[1],o[1]]; }
+                       return null;
+               }
+
+               private function parseCondition(s:String):Object {
+                       var o:Object=new Object();
+                       if      ((o=CONDITION_TRUE.exec(s)))  { return new Condition('true'     ,o[1]); }
+                       else if ((o=CONDITION_FALSE.exec(s))) { return new Condition('false',o[1]); }
+                       else if ((o=CONDITION_SET.exec(s)))   { return new Condition('set'      ,o[1]); }
+                       else if ((o=CONDITION_UNSET.exec(s))) { return new Condition('unset',o[1]); }
+                       else if ((o=CONDITION_NE.exec(s)))    { return new Condition('ne'       ,o[1],o[2]); }
+                       else if ((o=CONDITION_GT.exec(s)))    { return new Condition('>'        ,o[1],o[2]); }
+                       else if ((o=CONDITION_GE.exec(s)))    { return new Condition('>='       ,o[1],o[2]); }
+                       else if ((o=CONDITION_LT.exec(s)))    { return new Condition('<'        ,o[1],o[2]); }
+                       else if ((o=CONDITION_LE.exec(s)))    { return new Condition('<='       ,o[1],o[2]); }
+                       else if ((o=CONDITION_REGEX.exec(s))) { return new Condition('regex',o[1],o[2]); }
+                       else if ((o=CONDITION_EQ.exec(s)))    { return new Condition('eq'       ,o[1],o[2]); }
+                       return null;
+               }
+
+       }
+}
diff --git a/net/systemeD/halcyon/styleparser/PointRule.as b/net/systemeD/halcyon/styleparser/PointRule.as
deleted file mode 100644 (file)
index b053b66..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-package net.systemeD.halcyon.styleparser {
-
-       public class PointRule extends Rule {
-               public var pointStyle:PointStyle;
-               public var textStyle:TextStyle;
-
-               public function PointRule(c:Array=null,ps:PointStyle=null,ts:TextStyle=null) {
-                       conditions=c;
-                       pointStyle=ps;
-                       textStyle=ts;
-               }
-       }
-}
index 84d226a..e79916b 100644 (file)
@@ -1,12 +1,16 @@
 package net.systemeD.halcyon.styleparser {
 
-       public class PointStyle {
+       public class PointStyle extends Style {
 
-               public var icon:String;
-               public var width:uint;
-               public var height:uint;
-               public var sublayer:uint=0;
+               public var icon_image:String;
+               public var icon_width:uint;
+               public var icon_height:uint;
 
+               override public function get properties():Array {
+                       return [
+                               'icon_image','icon_width','icon_height'
+                       ];
+               }
        }
 
 }
index 4e24f64..061d1dd 100644 (file)
@@ -1,16 +1,22 @@
 package net.systemeD.halcyon.styleparser {
 
+    import net.systemeD.halcyon.connection.*;
+
        public class Rule {
 
-               public var conditions:Array;
-               public var breaker:Boolean = true;
+               public var conditions:Array = [];
                public var isAnd:Boolean = true;
-               public var minScale:uint = 19;
-               public var maxScale:uint = 13;
-               public var hasTags:Boolean = false;
-               public var setTags:Object = {};
+               public var minZoom:uint = 13;
+               public var maxZoom:uint = 19;
+               public var subject:String='';                   // "", "way", "node" or "relation"
+               
+               public function Rule(s:String=''):void {
+                       subject=s;
+               }
                
-               public function test(tags:Object):Boolean {
+               public function test(obj:Entity,tags:Object):Boolean {
+                       if (subject!='' && obj.getType()!=subject) { return false; }
+                       
                        var v:Boolean; var i:uint=0;
                        for each (var condition:Condition in conditions) {
                                var r:Boolean=condition.test(tags);
@@ -21,5 +27,9 @@ package net.systemeD.halcyon.styleparser {
                        }
                        return v;
                }
+               
+               public function toString():String {
+                       return subject+" z"+minZoom+"-"+maxZoom+": "+conditions;
+               }
        }
 }
\ No newline at end of file
index a7852a5..1d52f05 100644 (file)
@@ -4,100 +4,61 @@ package net.systemeD.halcyon.styleparser {
        import flash.events.*;
        import flash.net.*;
        import net.systemeD.halcyon.Globals;
-
+       import net.systemeD.halcyon.Map;
+    import net.systemeD.halcyon.connection.Entity;
+//     import bustin.dev.Inspector;
+       
        public class RuleSet {
 
-               public var rules:Array=new Array();             // list of rules
+               private var map:Map;
+               public var choosers:Array=new Array();  // list of StyleChoosers
                public var images:Object=new Object();  // loaded images
                private var iconCallback:Function=null; // function to call when all icons loaded
                private var iconsToLoad:uint=0;                 // number of icons left to load (fire callback when ==0)
 
                // variables for name, author etc.
 
-               public function RuleSet(f:Function=null):void {
+               public function RuleSet(m:Map,f:Function=null):void {
+                       map=m;
                        iconCallback=f;
                }
 
-               // returns array of styles
-               public function getStyles(isPoint:Boolean,tags:Object,scale:uint):Array {
-                       var rt:Object=tags; var rtused:Boolean=false;                   // new tag object to be returned
-                       var i:String;
-                       var styles:Array=[];
-                       for each (var rule:* in rules) {
-                               if ( isPoint && rule is ShapeRule) { continue; }
-                               if (!isPoint && rule is PointRule) { continue; }
-                               if (scale>rule.minScale) { continue; }
-                               if (scale<rule.maxScale) { continue; }
-                               if (rule.test(rt)) {
-                                       if (rule is ShapeRule && rule.shapeStyle)  { styles.push(rule.shapeStyle); }
-                                       if (rule is PointRule && rule.pointStyle)  { styles.push(rule.pointStyle); }
-                                       if (                     rule.textStyle )  { styles.push(rule.textStyle); }
-                                       if (rule is ShapeRule && rule.shieldStyle) { styles.push(rule.shieldStyle); }
-                                       if (rule.breaker) { break; }
-                                       if (rule.hasTags) {
-                                               // deep copy tag object so we're not modifying in situ
-                                               if (!rtused) { for (i in tags) { rt[i]=tags[i]; }; rtused=true; }
-                                               // and apply new tags
-                                               for (i in rule.setTags) { rt[i]=rule.setTags[i]; }
-                                       }
-                               }
-                       }
-                       return styles;
-               }
-
-               // Save and load rulesets
+               // Get styles for an object
 
-               public function save(url:String,name:String):void {
-
-                       var request:URLRequest=new URLRequest(url);
-                       var requestVars:URLVariables=new URLVariables();
-                       var loader:URLLoader=new URLLoader();
-                       
-                       // send to server
-                       requestVars['name']=name;
-                       requestVars['data']=YAML.encode(rules);
-                       request.data=requestVars;
-                       request.method = URLRequestMethod.POST;  
-                       loader.dataFormat = URLLoaderDataFormat.TEXT;
-                       loader.addEventListener(Event.COMPLETE,                                         savedRuleSet,                   false, 0, true);
-                       loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler,              false, 0, true);
-                       loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler,   false, 0, true);
-                       loader.addEventListener(IOErrorEvent.IO_ERROR,                          ioErrorHandler,                 false, 0, true);
-                       loader.load(request);
+               public function getStyles(obj:Entity,tags:Object):StyleList {
+                       var sl:StyleList=new StyleList();
+                       for each (var sc:StyleChooser in choosers) {
+                               sc.updateStyles(obj,tags,sl);
+                       }
+                       return sl;
                }
 
-               public function load(url:String):void {
+               // ---------------------------------------------------------------------------------------------------------
+               // Loading stylesheet
 
+               public function loadFromCSS(url:String):void {
                        var request:URLRequest=new URLRequest(url);
                        var loader:URLLoader=new URLLoader();
 
                        request.method=URLRequestMethod.GET;
                        loader.dataFormat = URLLoaderDataFormat.TEXT;
-                       loader.addEventListener(Event.COMPLETE,                                         loadedRuleSet,                  false, 0, true);
+                       loader.addEventListener(Event.COMPLETE,                                         loadedCSS,                              false, 0, true);
                        loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler,              false, 0, true);
                        loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler,   false, 0, true);
                        loader.addEventListener(IOErrorEvent.IO_ERROR,                          ioErrorHandler,                 false, 0, true);
                        loader.load(request);
                }
 
-               // data handlers
-               private function savedRuleSet(event:Event):void {
-                       var loader:URLLoader = URLLoader(event.target);  
-                       // do something with loader.data
-               }
-               
-               private function loadedRuleSet(event:Event):void {
-                       var loader:URLLoader = URLLoader(event.target);  
-                       rules=YAML.decode(event.target.data) as Array;
-                       // ** fire some event or other to tell map to redraw
-                       loadImages();
+               private function loadedCSS(event:Event):void {
+                       var css:MapCSS=new MapCSS(map);
+                       choosers=css.parse(event.target.data);
+//                     Inspector.getInstance().show();
+//                     Inspector.getInstance().shelf('Choosers', choosers);
                }
 
                private function httpStatusHandler( event:HTTPStatusEvent ):void { }
-               private function securityErrorHandler( event:SecurityErrorEvent ):void { }
-               private function ioErrorHandler( event:IOErrorEvent ):void { }
-               
-               // serialise/deserialise methods
+               private function securityErrorHandler( event:SecurityErrorEvent ):void { Globals.vars.root.addDebug("securityerrorevent"); }
+               private function ioErrorHandler( event:IOErrorEvent ):void { Globals.vars.root.addDebug("ioerrorevent"); }
 
 
                // ------------------------------------------------------------------------------------------------
@@ -108,16 +69,16 @@ package net.systemeD.halcyon.styleparser {
                public function loadImages():void {
                        var ps:PointStyle;
 
-                       for each (var rule:* in rules) {
-                               if (!(rule is PointRule)) { continue; }
+                       for each (var rule:* in choosers) {
+                               // if (!(rule is PointRule)) { continue; }
                                if (!(rule.pointStyle)) { continue; }
-                               if (!(rule.pointStyle.icon)) { continue; }
+                               if (!(rule.pointStyle.icon_image)) { continue; }
                                
                                iconsToLoad++;
-                               var request:URLRequest=new URLRequest(rule.pointStyle.icon);
+                               var request:URLRequest=new URLRequest(rule.pointStyle.icon_image);
                                var loader:ImageLoader=new ImageLoader();
                                loader.dataFormat=URLLoaderDataFormat.BINARY;
-                               loader.filename=rule.pointStyle.icon;
+                               loader.filename=rule.pointStyle.icon_image;
                                loader.addEventListener(Event.COMPLETE,                                         loadedImage,                    false, 0, true);
                                loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler,              false, 0, true);
                                loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler,   false, 0, true);
diff --git a/net/systemeD/halcyon/styleparser/ShapeRule.as b/net/systemeD/halcyon/styleparser/ShapeRule.as
deleted file mode 100644 (file)
index 59f4876..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package net.systemeD.halcyon.styleparser {
-
-       public class ShapeRule extends Rule {
-               public var shapeStyle:ShapeStyle;
-               public var textStyle:TextStyle;
-               public var shieldStyle:ShieldStyle;
-
-               public function ShapeRule(c:Array=null,ss:ShapeStyle=null,ts:TextStyle=null,hs:ShieldStyle=null) {
-                       conditions=c;
-                       shapeStyle=ss;
-                       textStyle=ts;
-                       shieldStyle=hs;
-               }
-       }
-}
index c00fd3c..59a13f0 100644 (file)
@@ -1,29 +1,30 @@
 package net.systemeD.halcyon.styleparser {
 
-       import flash.utils.ByteArray;
+       public class ShapeStyle extends Style {
 
-       public class ShapeStyle {
-
-               public var isStroked:Boolean=true;
-               public var stroke_width:Number;
-               public var stroke_colour:Number;
-               public var stroke_opacity:Number;
-               public var stroke_dashArray:Array=[];
-               public var stroke_linecap:String="none";
-               public var stroke_linejoin:String="round";
-               public var sublayer:uint=0;
+               public var width:Number;
+               public var color:Number;
+               public var opacity:Number;
+               public var dashes:Array;
+               public var linecap:String;
+               public var linejoin:String;
                
-               public var isFilled:Boolean=false;
-               public var fill_colour:Number;
+               public var fill_color:Number;
                public var fill_opacity:Number;
                public var fill_pattern:String;
                
-               public var isCased:Boolean=false;
                public var casing_width:Number;
-               public var casing_colour:Number;
+               public var casing_color:Number;
                public var casing_opacity:Number;
-               public var casing_dashArray:Array=[];
+               public var casing_dashes:Array;
                
+               override public function get properties():Array {
+                       return [
+                               'width','color','opacity','dashes','linecap','linejoin',
+                               'fill_color','fill_opacity','fill_pattern',
+                               'casing_width','casing_color','casing_opacity','casing_dashes'
+                       ];
+               }
        }
 
 }
index f243a82..fad84f0 100644 (file)
@@ -1,11 +1,16 @@
 package net.systemeD.halcyon.styleparser {
 
-       public class ShieldStyle extends TextStyle {
+       public class ShieldStyle extends Style {
 
-               public var shield:String;
+               public var shield_image:String;
                public var shield_width:uint;
                public var shield_height:uint;
+               // ** also needs shield fonts etc.
 
+               override public function get properties():Array {
+                       return [
+                               'shield_image','shield_width','shield_height'
+                       ];
+               }
        }
-
 }
diff --git a/net/systemeD/halcyon/styleparser/Style.as b/net/systemeD/halcyon/styleparser/Style.as
new file mode 100755 (executable)
index 0000000..7fce3dd
--- /dev/null
@@ -0,0 +1,58 @@
+package net.systemeD.halcyon.styleparser {
+
+       import flash.utils.ByteArray;
+       import flash.net.*;
+       import net.systemeD.halcyon.Globals;
+
+       public class Style {
+
+               public var merged:Boolean=false;
+               public var edited:Boolean=false;                // true once a property has been set from a string
+               public var sublayer:uint=5;
+
+               // Return an exact copy of this object
+               // ** this needs some benchmarking - may be quicker to iterate over .properties, copying each one
+
+               public function deepCopy():* {
+                       registerClassAlias("net.systemeD.halcyon.styleparser.ShapeStyle",ShapeStyle);
+                       registerClassAlias("net.systemeD.halcyon.styleparser.TextStyle",TextStyle);
+                       registerClassAlias("net.systemeD.halcyon.styleparser.PointStyle",PointStyle);
+                       registerClassAlias("net.systemeD.halcyon.styleparser.ShieldStyle",ShieldStyle);
+                       registerClassAlias("net.systemeD.halcyon.styleparser.InstructionStyle",InstructionStyle);
+                       var a:ByteArray=new ByteArray();
+                       a.writeObject(this);
+                       a.position=0;
+                       return (a.readObject());
+               }
+
+               // Add properties from another object
+
+               public function mergeWith(additional:Style):void {
+                       for each (var prop:String in properties) {
+                               if (additional[prop]) {
+                                       this[prop]=additional[prop];
+                               }
+                       }
+                       this.merged=true;
+               }
+
+               // Getter (to be overridden) for property list
+
+               public function get properties():Array {
+                       return [];
+               }
+
+               // Set property and cast as correct type (used in stylesheet imports)
+               
+               public function setPropertyFromString(k:String,v:*):Boolean {
+                       if (!this.hasOwnProperty(k)) { return false; }
+                       // ** almost certainly need to do more here, e.g. true|1|yes=Boolean true
+                       switch (typeof(this[k])) {
+                               case "number":  this[k]=Number(v) ; edited=true; return true;
+                               case "string":  this[k]=String(v) ; edited=true; return true;
+                               case "boolean": this[k]=Boolean(v); edited=true; return true;
+                       }
+                       return false;
+               }
+       }
+}
diff --git a/net/systemeD/halcyon/styleparser/StyleChooser.as b/net/systemeD/halcyon/styleparser/StyleChooser.as
new file mode 100755 (executable)
index 0000000..46f5aff
--- /dev/null
@@ -0,0 +1,138 @@
+package net.systemeD.halcyon.styleparser {
+
+       import net.systemeD.halcyon.connection.Entity;
+       import net.systemeD.halcyon.Globals;
+
+       public class StyleChooser {
+
+               /*
+                       A StyleChooser object is equivalent to one CSS selector+declaration.
+
+                       Its ruleChains property is an array of all the selectors, which would
+                       traditionally be comma-separated. For example:
+                               h1, h2, h3 em
+                       is three ruleChains.
+                       
+                       Each ruleChain is itself an array of nested selectors. So the above 
+                       example would roughly be encoded as:
+                               [[h1],[h2],[h3,em]]
+                                 ^^   ^^   ^^ ^^   each of these is a Rule
+
+                       The styles property is an array of all the style objects to be drawn
+                       if any of the ruleChains evaluate to true.
+
+               */
+
+               public var ruleChains:Array=[[]];               // array of array of Rules
+               public var styles:Array=[];                             // array of ShapeStyle/ShieldStyle/TextStyle/PointStyle
+
+               private var rcpos:uint=0;
+               private var stylepos:uint=0;
+
+               // Update the current StyleList from this StyleChooser
+
+               public function updateStyles(obj:Entity, tags:Object, sl:StyleList):void {
+
+                       // Are any of the ruleChains fulfilled?
+                       // ** needs to cope with min/max zoom
+                       var fulfilled:Boolean=false;
+                       for each (var c:Array in ruleChains) {
+                               if (testChain(c,-1,obj,tags)) {
+Globals.vars.root.addDebug("object "+obj+", testChain "+c+" returned true"); 
+                                       fulfilled=true; break;
+                               }
+                       }
+                       if (!fulfilled) { return; }
+
+                       // Update StyleList
+                       for each (var r:Style in styles) {
+                               var a:*;
+                               if      (r is ShapeStyle ) { a=sl.shapeStyles; }
+                               else if (r is ShieldStyle) { a=sl.shieldStyles; }
+                               else if (r is TextStyle  ) { a=sl.textStyles;  }
+                               else if (r is PointStyle ) { a=sl.pointStyles; }
+                               else if (r is InstructionStyle) {
+                                       if (InstructionStyle(r).breaker) { return; }
+                                       if (InstructionStyle(r).set_tags) {
+                                               for (var k:String in InstructionStyle(r).set_tags) { tags[k]=InstructionStyle(r).set_tags[k]; }
+                                       }
+                                       continue;
+                               }
+
+Globals.vars.root.addDebug("style on "+r.sublayer); 
+                               if (a[r.sublayer]) {
+                                       // If there's already a style on this sublayer, then merge them
+                                       // (making a deep copy if necessary to avoid altering the root style)
+                                       if (!a[r.sublayer].merged) { a[r.sublayer]=a[r.sublayer].deepCopy(); }
+                                       a[r.sublayer].mergeWith(r);
+Globals.vars.root.addDebug("merging style on sl"+r.sublayer); 
+                               } else {
+                                       // Otherwise, just assign it
+                                       a[r.sublayer]=r;
+Globals.vars.root.addDebug("assigning style on sl"+r.sublayer); 
+                               }
+                       }
+               }
+
+
+               // Test a ruleChain
+               // - run a set of tests in the chain
+               //              works backwards from at position "pos" in array, or -1  for the last
+               //              separate tags object is required in case they've been dynamically retagged
+               // - if they fail, return false
+               // - if they succeed, and it's the last in the chain, return happily
+               // - if they succeed, and there's more in the chain, rerun this for each parent until success
+               
+               private function testChain(chain:Array,pos:int,obj:Entity,tags:Object):Boolean {
+                       if (pos==-1) { pos=chain.length-1; }
+
+                       var r:Rule=chain[pos];
+                       if (!r.test(obj, tags)) { return false; }
+                       if (pos==0) { return true; }
+                       
+                       for each (var p:Entity in obj.parentObjects) {
+                               if (testChain(chain.slice(0), pos-1, p, p.getTagsHash() )) { return true; }
+                       }
+                       return false;
+               }
+               
+               
+               // ---------------------------------------------------------------------------------------------
+               // Methods to add properties (used by parsers such as MapCSS)
+               
+               // newGroup             <- starts a new ruleChain in this.ruleChains
+               public function newGroup():void {
+Globals.vars.root.addDebug("* newGroup");
+                       if (ruleChains[rcpos].length>0) {
+                               ruleChains[++rcpos]=[];
+                       }
+               }
+
+               // newObject    <- adds into the current ruleChain (starting a new Rule)
+               public function newObject(e:String=''):void {
+Globals.vars.root.addDebug("* newObject");
+                       ruleChains[rcpos].push(new Rule(e));
+               }
+
+               // addZoom              <- adds into the current ruleChain (existing Rule)
+               public function addZoom(z1:uint,z2:uint):void {
+Globals.vars.root.addDebug("* addZoom");
+                       ruleChains[rcpos][ruleChains[rcpos].length-1].minZoom=z1;
+                       ruleChains[rcpos][ruleChains[rcpos].length-1].minZoom=z2;
+               }
+               
+               // addCondition <- adds into the current ruleChain (existing Rule)
+               public function addCondition(c:Condition):void {
+Globals.vars.root.addDebug("* addCondition");
+                       ruleChains[rcpos][ruleChains[rcpos].length-1].conditions.push(c);
+Globals.vars.root.addDebug("    now "+ruleChains[rcpos][ruleChains[rcpos].length-1].conditions);
+               }
+
+               // addStyles    <- adds to this.styles
+               public function addStyles(a:Array):void {
+Globals.vars.root.addDebug("* Styles ("+ruleChains[0][0].conditions+")");
+                       styles=styles.concat(a);
+               }
+               
+       }
+}
diff --git a/net/systemeD/halcyon/styleparser/StyleList.as b/net/systemeD/halcyon/styleparser/StyleList.as
new file mode 100755 (executable)
index 0000000..5ee42bc
--- /dev/null
@@ -0,0 +1,20 @@
+package net.systemeD.halcyon.styleparser {
+
+       public class StyleList {
+
+               /*
+                       A StyleList object is the full list of all styles applied to 
+                       a drawn entity (i.e. node/way).
+                       
+                       Each array element applies to that sublayer (z-index). If there
+                       is no element, nothing is drawn on that sublayer.
+
+               */
+
+               public var shapeStyles:Array=[];
+               public var textStyles:Array=[];
+               public var pointStyles:Array=[];
+               public var shieldStyles:Array=[];
+
+       }
+}
\ No newline at end of file
index eb2b2a5..2eeb9d9 100644 (file)
@@ -6,34 +6,42 @@ package net.systemeD.halcyon.styleparser {
        import flash.filters.BitmapFilter;
        import flash.filters.GlowFilter;
 
-       public class TextStyle {
+       public class TextStyle extends Style {
 
-               public var font_name:String;
+               public var font_family:String;
                public var font_bold:Boolean;
                public var font_italic:Boolean;
                public var font_caps:Boolean;
-               public var text_size:uint;
-               public var text_colour:uint;
-               public var text_offset:Number;  // Y offset - i.e. within or outside casing?
-               public var text_width:Number;   // maximum width of label
-               public var tag:String;
-               public var pullout_colour:uint;
-               public var pullout_radius:uint=0;
-               public var isLine:Boolean;
-               public var sublayer:uint=0;
+               public var font_size:uint;
+               public var text_color:uint;
+               public var text_offset:Number;          // Y offset - i.e. within or outside casing?
+               public var max_width:Number;            // maximum width of label
+               public var text:String;
+               public var text_halo_color:uint;
+               public var text_halo_radius:uint=0;
+               public var text_onpath:Boolean;         // true if on line
 
+               override public function get properties():Array {
+                       return [
+                               'font_family','font_bold','font_italic','font_caps','font_size',
+                               'text_color','text_offset','max_width',
+                               'text','text_halo_color','text_halo_radius','text_onpath'
+                       ];
+               }
+
+               
                public function getTextFormat():TextFormat {
-                       return new TextFormat(font_name   ? font_name : "DejaVu",
-                                                                 text_size   ? text_size : 8,
-                                                                 text_colour ? text_colour: 0,
-                                                                 font_bold   ? font_bold : false,
+                       return new TextFormat(font_family ? font_family: "DejaVu",
+                                                                 font_size   ? font_size  : 8,
+                                                                 text_color  ? text_color : 0,
+                                                                 font_bold   ? font_bold  : false,
                                                                  font_italic ? font_italic: false);
                }
        
-               public function getPulloutFilter():Array {
-                       var filter:BitmapFilter=new GlowFilter(pullout_colour ? pullout_colour : 0xFFFFFF,1,
-                                                                                                  pullout_radius ? pullout_radius: 2,
-                                                                                                  pullout_radius ? pullout_radius: 2,255);
+               public function getHaloFilter():Array {
+                       var filter:BitmapFilter=new GlowFilter(text_halo_color  ? text_halo_color  : 0xFFFFFF,1,
+                                                                                                  text_halo_radius ? text_halo_radius: 2,
+                                                                                                  text_halo_radius ? text_halo_radius: 2,255);
                        return [filter];
                }
                
@@ -44,15 +52,15 @@ package net.systemeD.halcyon.styleparser {
                        tf.embedFonts = true;
                        tf.defaultTextFormat = n;
                        tf.text = a;
-                       if (text_width) {
-                               tf.width=text_width;
+                       if (max_width) {
+                               tf.width=max_width;
                                tf.wordWrap=true;
                                tf.height=tf.textHeight+4;
                        } else {
                                tf.width = tf.textWidth+4;
                                tf.height = tf.textHeight+4;
                        }
-                       if (pullout_radius>0) { tf.filters=getPulloutFilter(); }
+                       if (text_halo_radius>0) { tf.filters=getHaloFilter(); }
                        d.x=x-tf.width/2;
                        d.y=y+(text_offset ? text_offset : 0);
                        d.addChild(tf);
diff --git a/resources/test.css b/resources/test.css
new file mode 100644 (file)
index 0000000..6f09121
--- /dev/null
@@ -0,0 +1,9 @@
+way[name=~/Brown/] { set .highlighted; }
+way[highway=secondary] { color: green; width: 5; }
+way[highway=unclassified] { color: blue; width: 3; }
+way[highway=residential] { color: blue; width: 2; }
+way[highway=primary] { color: red; width: 5; }
+way[highway=trunk] { color: green; width: 7; }
+way .highlighted { color: pink; }
+way :hover     { z-index: 2; width: 8; color: gray; }
+way :selected { z-index: 2; width: 8; color: yellow; }