/** * vim:et sts=4 sw=4 cindent: * $Id: MapComponent.as 734 2008-12-21 23:43:44Z tom $ * @author allens */ package com.modestmaps.flex { /** * The flex.MapComponent class is an ActionScript UI component. To use it in your application, * simply specify a new namespace in the root node of your application. As long as the * com.modestmaps.flex namespace is in your path, Flex Builder should find the class * and auto-complete the element name once you've opened a new tag and typed the namespace. * * * * * * The MXML component doesn't currently support the full com.modestmaps.Map API, but the * instance of that class is accessible via the (read-only) "map" getter if you need to * call any of its methods. */ import com.modestmaps.Map; import com.modestmaps.core.*; import com.modestmaps.events.MapEvent; import com.modestmaps.geo.*; import com.modestmaps.mapproviders.*; import com.modestmaps.mapproviders.microsoft.*; import com.modestmaps.mapproviders.yahoo.*; import flash.events.Event; import mx.core.UIComponent; [Event(name="startZooming", type="com.modestmaps.events.MapEvent")] [Event(name="stopZooming", type="com.modestmaps.events.MapEvent")] [Event(name="zoomedBy", type="com.modestmaps.events.MapEvent")] [Event(name="startPanning", type="com.modestmaps.events.MapEvent")] [Event(name="stopPanning", type="com.modestmaps.events.MapEvent")] [Event(name="panned", type="com.modestmaps.events.MapEvent")] [Event(name="resized", type="com.modestmaps.events.MapEvent")] [Event(name="mapProviderChanged",type="com.modestmaps.events.MapEvent")] [Event(name="beginExtentChange", type="com.modestmaps.events.MapEvent")] [Event(name="extentChanged", type="com.modestmaps.events.MapEvent")] [Event(name="beginTileLoading", type="com.modestmaps.events.MapEvent")] [Event(name="allTilesLoaded", type="com.modestmaps.events.MapEvent")] [Event(name="rendered", type="com.modestmaps.events.MapEvent")] [Event(name="markerRollOver", type="com.modestmaps.events.MarkerEvent")] [Event(name="markerRollOut", type="com.modestmaps.events.MarkerEvent")] [Event(name="markerClick", type="com.modestmaps.events.MarkerEvent")] public class MapComponent extends UIComponent { public static const DEFAULT_MEASURED_WIDTH:Number = 400; public static const DEFAULT_MEASURED_MIN_WIDTH:Number = 100; public static const DEFAULT_MEASURED_HEIGHT:Number = 400; public static const DEFAULT_MEASURED_MIN_HEIGHT:Number = 100; public static const DEFAULT_MAX_WIDTH:Number = 10000; public static const DEFAULT_MAX_HEIGHT:Number = 10000; public static const DEFAULT_MAP_PROVIDER:IMapProvider = new BlueMarbleMapProvider(); protected var _map:Map; protected var mapInitDirty:Boolean = true; public function MapComponent() { super(); } /** * Since we're not yet supporting the full Map interface, * make the instance gettable, read-only. */ public function get map():com.modestmaps.Map { return _map; } override protected function createChildren():void { trace("Map.createChildren()"); super.createChildren(); if (mapInitDirty && _map == null) { // TODO: implement draggable switch? //trace(' * initializing map: ' + w + 'x' + h + ', ' + _draggable + ', provider: ' + _mapProvider.toString()); //_map.init(w, h, _draggable, _mapProvider || DEFAULT_MAP_PROVIDER); _map = new Map(unscaledWidth, unscaledHeight, _draggable, _mapProvider || DEFAULT_MAP_PROVIDER); addChild(_map); mapProviderDirty = false; mapInitDirty = false; } } override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { if (_map.getWidth() != unscaledWidth || _map.getHeight() != unscaledHeight) { _map.setSize(unscaledWidth, unscaledHeight); } // save extent setting until the map has a valid size if (mapExtentDirty && _map.getWidth() > 0 && _map.getHeight() > 0) { trace(' * extent is dirty, setting to: ' + _extent); _map.setExtent(_extent); mapExtentDirty = false; } } protected var mapExtentDirty:Boolean = false; protected var _extent:MapExtent; protected var _mapExtentString:String; protected var mapCenterDirty:Boolean = true; protected var _centerLocation:Location = new Location(0, 0); protected var mapZoomDirty:Boolean = true; protected var _zoom:int = 1; /** * The "extent" setter accepts either a MapExtent instance or a String; * the latter is converted into a MapExtent using the static fromString() * method. This allows the extent to be defined as a string in MXML * attributes, a la "north, south, east, west". */ [Inspectable(category="MapComponent")] public function set extent(mapExtent:*):void { if (mapExtent is String) { // TODO: try/catch MapExtent.fromString() mapExtent = MapExtent.fromString(mapExtent); } if (!(mapExtent is MapExtent)) { throw new Error("Invalid extent supplied"); } trace("got extent: " + mapExtent); _extent = mapExtent; mapExtentDirty = true; mapCenterDirty = false; mapZoomDirty = false; invalidateProperties(); } public function get extent():MapExtent { return _map ? _map.getExtent() : _extent; } /** * Like the "extent" setter, the "center" setter accepts a String in addition to * a Location object, so that locations can be specified in MXML attributes as * strings ("lat, lon"). */ [Inspectable(category="Map", defaultValue="0,0")] public function set center(location:*):void { if (location is String) { location = Location.fromString(location); } if (!(location is Location)) { throw new Error("Invalid location supplied"); } _centerLocation = location; mapCenterDirty = true; mapExtentDirty = false; invalidateProperties(); } public function get center():Location { return _map ? _map.getCenter() : _centerLocation; } public function set zoom(zoomLevel:int):void { _zoom = zoomLevel; mapZoomDirty = true; mapExtentDirty = false; invalidateProperties(); } protected var mapProviderDirty:Boolean = true; protected var _mapProvider:IMapProvider = DEFAULT_MAP_PROVIDER; /** * The "provider" setter accepts either a String (Flex Builder should provide * a list of valid values per the Inspectable() metadata tag) or an IMapProvider * instance. You can specify the latter in MXML by wrapping the constructor in * braces: * * */ [Inspectable(category="Map", enumeration="BLUE_MARBLE,MICROSOFT_AERIAL,MICROSOFT_ROAD,MICROSOFT_HYBRID,YAHOO_ROAD,YAHOO_AERIAL,YAHOO_HYBRID,OPEN_STREET_MAP", defaultValue="BLUE_MARBLE")] public function set provider(provider:*):void { if(provider is IMapProvider) { _mapProvider = provider; } else { switch(provider) { case "BLUE_MARBLE": _mapProvider = new BlueMarbleMapProvider(); break; case "OPEN_STREET_MAP": _mapProvider = new OpenStreetMapProvider(); break; case "MICROSOFT_AERIAL": _mapProvider = new MicrosoftAerialMapProvider(); break; case "MICROSOFT_HYBRID": _mapProvider = new MicrosoftHybridMapProvider(); break; case "MICROSOFT_ROAD": _mapProvider = new MicrosoftRoadMapProvider(); break; case "YAHOO_AERIAL": _mapProvider = new YahooAerialMapProvider(); break; case "YAHOO_HYBRID": _mapProvider = new YahooHybridMapProvider(); break; case "YAHOO_ROAD": _mapProvider = new YahooRoadMapProvider(); break; } } mapProviderDirty = true; invalidateProperties(); } public function get provider():IMapProvider { if (_map) { var provider:IMapProvider = _map.getMapProvider(); return provider ? provider : _mapProvider; } else { return _mapProvider; } } protected var _draggable:Boolean = true; /** * Currently the "draggable" setter will only work pre-initialization. * In other words, setting draggable after the component has been * initialized will have no effect; it's provided merely as a means for * setting the property in MXML. */ [Inspectable(category="Map")] public function set draggable(isDraggable:Boolean):void { trace('draggable', isDraggable); if (initialized) { throw new Error("'draggable' is not settable post initialization"); } else { _draggable = isDraggable; } } public function get draggable():Boolean { return _draggable; } /** * Updates the map's provider, extent or center/zoom, and size. This is called * by the Flex framework when necessary. There's probably some more optimization that * could be done in the whole invalidation/validation/update process; for instance, * a flag set in invalidateSize() could be used to determine whether or not we should * call _map.setSize(), rather than just comparing the size. */ // http://ccgi.arutherford.plus.com/blog/wordpress/?p=169 override protected function commitProperties():void { trace('commitProperties()', this.id); if (_map!=null) { if (mapZoomDirty) { trace (' * zoom is dirty...'); _map.setZoom(_zoom); mapZoomDirty = false; } if (mapCenterDirty) { trace (' * center is dirty...'); _map.setCenter(_centerLocation); mapCenterDirty = false; } if (mapExtentDirty && _map.getWidth() > 0 && _map.getHeight() > 0) { trace(' * extent is dirty, setting to: ' + _extent); _map.setExtent(_extent); mapExtentDirty = false; } if (mapProviderDirty) { trace(' * setting map provider: ' + _mapProvider.toString()); _map.setMapProvider(_mapProvider); mapProviderDirty = false; } } super.commitProperties(); } } }