Friday, December 14, 2007
Minn IT Symposium
Sorry for the delay folks. As promised, you can download the presentation and the samples. Thank you for all that attended, and hope to hear from you.
Thursday, December 13, 2007
The Flex Show
Listen to me being interviewed on The Flex Show. I'm talking (with my very heavy accent and my rolling r's :-) about AWX, Spring, the Minneapolis bridge application and lot's of other stuff. Tell me what you think. I had a great time doing it.
Tuesday, November 27, 2007
Java To AS3 on AIR
Found this great project that enables me to convert Java code to ActionScript3 code, or at least get me close enough that I can do minor manual tweaks to get code to compile (granted that compiled code does not mean that it runs :-). However, in its current format, it was a laborious process as I can only process one file at the time, and had to copy and paste the generated code from the text area into a file. There must be a better way ! And yes there is a better way using Adobe AIR. AIR enables me to read from and write to the local file system. So, I took the core Converter class and wrapped it with the AIR File api.
A user can now select the output folder and upon the selection of the input folder, all the java files will be read, converted and written to the output folder. In addition, you can drag and drop onto the output or input button the output or input folder.
Download the AIR file from here. BTW - this is using the latest beta3 version :-)
Granite Data Services
I love Life Cycle Data Services. As a developer, IMHO, I think it is worth its weight in gold. However, I do get customers that want to do remoting with it :-( So...since HTTPService and WebService are first class citizens in Flex, I recommend that they SOAP enable their server side services, or come up with some kind of XML representation and XML over HTTP since XML is a native type in AS3. But the serialization/deserializtion is costly, and it would be great if we can use the flash native binary format (AMF) to transfer objects around. Thus enters Granite. Quoting from the introduction "Granite Data Services (GDS) is a free, open source (LGPL'd), alternative to Adobe® LiveCycle® (Flex™ 2) Data Services for J2EE application servers. It is not, however, a drop-in replacement: you won't be able to simply deploy a Flex 2 Data Services application into a Granite Data Services server without modifications. The main goal of this project is to provide a framework for Flex 2/EJB3/Spring/Pojo application development with full AMF3/RemoteObject benefits."
So, if you are a java server side programmer, try it out. In addition, if you use Spring to manage your POJOs, then client side RemoteObjects can have a destination that is bound to a spring bean.
So, if you are a java server side programmer, try it out. In addition, if you use Spring to manage your POJOs, then client side RemoteObjects can have a destination that is bound to a spring bean.
Friday, November 23, 2007
GeoRSS Support
AWX has GeoRSS support. So given for example the following feed (on my local machine I can access it as http://ccccmac/~mansour/geofeed.xml):
You can create a GeoRSS layer as follows:
In this example, I'm setting the GeoRSSLayer feed URL, and I have the option to set the style of the polygon, in this case a yellow filling with a 2 pixel wide black outline. On each added overlay, I'm adding a mouse down event listener, such that when I click on the overlay, I navigate to the associated URL. When the feed is fully loaded, I'm setting the map extent to the layer extent plus a 2 percent buffer.
Remember, if you want to access a remote GeoRSS feed, make sure that it has a crossdomain.xml file or you can always use a proxy server.
<?xml version="1.0"?>
<rss xmlns:georss="http://www.georss.org/georss" version="2.0">
<channel>
<title>GeoRSS-Simple Example</title>
<description>Example GeoRSS-Simple Feed</description>
<georss:box>38.373576,-122.854022,38.423121,-122.804327</georss:box>
<item>
<title>GeoRSS-Simple Polygon</title>
<description>A polygon describes an arbitrary geographic shape. The
format is a series of ordered latitude, longitude pairs. The last pair
must be the same as the first pair, closing the polygon.</description>
<guid>urn:uuid:d496f4e3-7fd8-4169-ac6208e36dffa7f0</guid>
<link>http://georss.org/simple</link>
<author>georss supporter</author>
<pubDate>Mon, 09 Jul 2007 10:49:00 PDT</pubDate>
<georss:polygon>
45.256 -110.45 46.46 -109.48 43.84 -109.86 45.256 -110.45
</georss:polygon>
</item>
</channel>
</rss>
You can create a GeoRSS layer as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:awx="http://www.arcwebservices.com/2007/awx"
layout="absolute"
xmlns:layers="com.esri.aws.awx.map.layers.*">
<mx:Script>
<![CDATA[
import flash.net.navigateToURL;
import mx.events.ChildExistenceChangedEvent;
import com.esri.aws.awx.map.layers.overlays.OverlayObject;
private function onChildAdd( event : ChildExistenceChangedEvent ) : void
{
var overlayObject : OverlayObject = event.relatedObject as OverlayObject;
overlayObject.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, false, 0.0, true);
}
private function onMouseDown( event : MouseEvent ) : void
{
var overlayObject : OverlayObject = event.target as OverlayObject;
navigateToURL( new URLRequest( overlayObject.link), "_blank");
}
private function onFeedLoad() : void
{
map.extent = geoRSSLayer.extent.createBuffer(0.2);
}
]]>
</mx:Script>
<awx:Framework/>
<awx:Map id="map" createDefaultLayers="false">
<layers:GeoRSSLayer
id="geoRSSLayer"
feedUrl="http://ccccmac/~mansour/geofeed.xml"
feedLoad="onFeedLoad()"
childAdd="onChildAdd(event)"
>
<layers:polygonStyle>
<awx:PolygonStyle color="0xFFFF00" outlineColor="0x000000" outlineThickness="2"/>
</layers:polygonStyle>
</layers:GeoRSSLayer>
</awx:Map>
</mx:Application>
In this example, I'm setting the GeoRSSLayer feed URL, and I have the option to set the style of the polygon, in this case a yellow filling with a 2 pixel wide black outline. On each added overlay, I'm adding a mouse down event listener, such that when I click on the overlay, I navigate to the associated URL. When the feed is fully loaded, I'm setting the map extent to the layer extent plus a 2 percent buffer.
Remember, if you want to access a remote GeoRSS feed, make sure that it has a crossdomain.xml file or you can always use a proxy server.
Thursday, November 22, 2007
10000 Markers
Been wanting to blog about this for a while. Users been asking me "how to I plot thousands of markers without the overhead of a Marker (which is a subclass of UIComponent) but still get the benefits of Marker mostly for mouse over/out/click events ?". Back in the days when I was RAT (Rent-a-Tech), my friend Leo came up with a concept of TurboLayer for SDE, where you group in one feature all the geometries of a feature class. It was fast, but you could not get to an individual feature because it was all "blob'ed" together (SDE folks out there, you know what I mean :-) So I took the same concept and implemented it in AWX, but with an extra twist where you can get to an individual feature indirectly using an RTree class. An RTree enables you to efficiently index features spatially on the client side. cool, eh ? You can download this zip file that contains an application that loads 1000 random points and when you mouse over a point, a tooltip shows up with the id of the point. If there are more that one point at that mouse location, then the tooltip text is a list of ids separated by '/'. I've tried this on 10000 point and did not notice a performance issue - this is due to the efficient implementation of the RTree (thanks Kerry :-). So...download the file and try it out and tell me what you think. Happy Thanksgiving.
Wednesday, November 21, 2007
DefaultProperty metadata
Looking back at the last post, yea... it is nice to MXML, but it is a bit ugly and verbose till I remembered the [DefaultProperty("xxxx")] metadata. Adjusted my local copy and what you will get is this:
WDYT ? oh and BTW, hoping to put this for the next release :-)
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:awx="http://www.arcwebservices.com/v2007/awx"
layout="absolute"
>
<awx:Framework/>
<awx:Map createDefaultLayers="false">
<awx:PolygonLayer>
<awx:Polygon>
<awx:PolygonShape>
<awx:GeoPoint x="45" y="45"/>
<awx:GeoPoint x="45" y="-45"/>
<awx:GeoPoint x="-45" y="0"/>
</awx:PolygonShape>
</awx:Polygon>
</awx:PolygonLayer>
</awx:Map>
</mx:Application>
WDYT ? oh and BTW, hoping to put this for the next release :-)
Blogged with Flock
Tuesday, November 20, 2007
Creating polygons in MXML
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
xmlns:geom="com.esri.aws.awx.geom.*"
xmlns:arcwebservices="http://www.arcwebservices.com/2007/awx"
>
<arcwebservices:Framework/>
<arcwebservices:Map>
<arcwebservices:basemaps>
<arcwebservices:PolygonLayer>
<arcwebservices:Polygon>
<arcwebservices:shape>
<geom:PolygonShape>
<geom:geoPoints>
<geom:GeoPoint x="45" y="45"/>
<geom:GeoPoint x="45" y="-45"/>
<geom:GeoPoint x="-45" y="45"/>
</geom:geoPoints>
</geom:PolygonShape>
</arcwebservices:shape>
</arcwebservices:Polygon>
</arcwebservices:PolygonLayer>
</arcwebservices:basemaps>
</arcwebservices:Map>
</mx:Application>
Sunday, November 18, 2007
AWX 4.0 Is Released !
Check out the latest version 4.0 - lots of bug fixes - great documentation. And if you are wondering what happened to version 3.0, well...it was an internal release. Have fun :-)
Blogged with Flock
Friday, November 16, 2007
IntelliJ 7 Rocks
I've been using IDEA since I do not remember when. Just got a license to the latest version. It has great Spring support, and now you can see your web.xml files in this wizard cool panes. In addition, there is support of AS3, I mean with auto-completion and refactoring. Too cool !
Blogged with Flock
Cairngorm and the Framework
People have been asking me to show them how to use Cairngorm with the AWX Framework that is loosely based on OSGi. The interaction will most likely come in an ICommand implementation or a Delegate. Here is an example that shows how to use the Find Service.
First, make sure that you have registered with the Framework the FindActivator.
And here is the FindEvent class:
First, make sure that you have registered with the Framework the FindActivator.
<Framework apiKey="[your-api-key]">Next, since a Command is always created and executed by the CairngormEventDispatcher, you can always get a reference to the Framework through its static function getInstance() and then get a reference to systemBundleContext which is an IBundleContext implementation.
<FindActivator/>
</Framework>
package com.esri.awx
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import com.esri.aws.osgi.framework.Framework;
import com.esri.aws.osgi.framework.IBundleContext;
import com.esri.aws.osgi.framework.IServiceReference;
import com.esri.aws.services.IFind;
import flash.utils.getQualifiedClassName;
import mx.collections.ItemResponder;
public class FindCommand implements ICommand
{
public function execute(event:CairngormEvent):void
{
var findEvent : FindEvent = event as FindEvent;
var context : IBundleContext = Framework.getInstance().systemContext;
var ref : IServiceReference = context.getServiceReference(getQualifiedClassName(IFind));
if( ref )
{
var find : IFind = context.getService( ref ) as IFind;
find.findLocation( findEvent.location, null, new ItemResponder(
function( data : Object, token : Object = null) : void {
// do something with data
},
function( info : Object, token : Object = null) : void {
// do something with info
}
));
}
}
}
}
And here is the FindEvent class:
package com.esri.awx
{
import com.adobe.cairngorm.control.CairngormEvent;
public class FindEvent extends CairngormEvent
{
public static const FIND : String = "find";
public var location : String;
public function FindEvent(
type:String,
bubbles:Boolean=false,
cancelable:Boolean=false
)
{
super(type, bubbles, cancelable);
}
}
}
Blogged with Flock
Sunday, November 11, 2007
Thursday, October 25, 2007
How to find locations ?
So...if you want to find "380 new york st, redlands, ca" or "www.esri.com" or "909 793 285", you can use the IFind service.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:services="com.esri.aws.services.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import com.esri.aws.services.FindOptions;
import com.esri.aws.services.FindResultSet;
import com.esri.aws.services.GeocodeInfo;
import mx.controls.Alert;
import mx.collections.ItemResponder;
import com.esri.aws.services.IFind;
import flash.utils.getQualifiedClassName;
import com.esri.aws.osgi.framework.IServiceReference;
import com.esri.aws.osgi.framework.IBundleContext;
private function doFind() : void
{
var context : IBundleContext = framework.systemContext;
var ref : IServiceReference = context.getServiceReference(getQualifiedClassName(IFind));
if( ref == null)
{
Alert.show( "No service reference !");
return;
}
var find : IFind = context.getService( ref ) as IFind;
find.findLocation( textInput.text, null, new ItemResponder(
function( data : Object, token : Object = null) : void
{
var findResultSet : FindResultSet = FindResultSet(data);
arrayCollection = new ArrayCollection( findResultSet.findResults );
},
function( info : Object, token : Object = null) : void
{
Alert.show( info.toString());
}
));
}
]]>
</mx:Script>
<framework:Framework id="framework"
apiKey="XXXXX">
<services:FindActivator/>
</framework:Framework>
<mx:ArrayCollection id="arrayCollection"/>
<mx:Panel width="100%" height="100%">
<mx:DataGrid width="100%" height="100%" dataProvider="{arrayCollection}"/>
<mx:ControlBar>
<mx:TextInput id="textInput" enter="doFind()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
Wednesday, October 24, 2007
Flickr Images as Markers
You can create BubbleMarkers that when expanded will display a Flickr image. In the below example, I'm creating a map, and when the user clicks on the search button, I'm searching any public image in the map extent.
And here is the Photo class
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import mx.controls.Image;
import com.esri.aws.awx.geom.PointShape;
import com.esri.aws.awx.map.layers.overlays.BubbleMarker;
import mx.controls.Alert;
import com.esri.flickr.Photo;
import mx.collections.ItemResponder;
import mx.rpc.AsyncToken;
import mx.rpc.http.HTTPService;
import com.esri.aws.awx.geom.Extent;
private function searchPhotos() : void
{
map.markerLayer.removeAllOverlays();
var extent : Extent = map.extent;
var httpService : HTTPService = new HTTPService();
var urlVar : URLVariables = new URLVariables();
urlVar.api_key = "19640523";
urlVar.method="flickr.photos.search";
urlVar.extras = "geo";
urlVar.bbox = extent.minX+","+extent.minY+","+extent.maxX+","+extent.maxY;
httpService.request = urlVar;
httpService.resultFormat = HTTPService.RESULT_FORMAT_E4X;
httpService.url = "http://api.flickr.com/services/rest/";
var async : AsyncToken = httpService.send();
async.addResponder( new ItemResponder(
function( data : Object, token : Object = null) : void {
var rsp : XML = data.result as XML;
if( rsp.@stat == "ok")
{
for each ( var photos : XMLList in rsp.photos)
{
for each ( var photoXML : XML in photos.photo)
{
var photo : Photo = new Photo();
photo.id = photoXML.@id;
photo.owner = photoXML.@owner;
photo.secret = photoXML.@secret;
photo.server = photoXML.@server;
photo.farm = photoXML.@farm;
photo.title = photoXML.@title;
photo.isPublic = photoXML.@ispublic;
photo.isFriend = photoXML.@isfriend;
photo.isFamily = photoXML.@isfamily == "1";
photo.latitude = photoXML.@latitude;
photo.longitude = photoXML.@longitude;
photo.accuracy = photoXML.@accuracy;
addPhotoMarker( photo );
}
}
}
},
function( info : Object, token : Object = null) : void
{
Alert.show( info.toString());
}
));
}
private function addPhotoMarker( photo : Photo ) : void
{
var pointShape : PointShape = new PointShape( photo.longitude, photo.latitude);
var bubbleMarker : BubbleMarker = new BubbleMarker( pointShape );
bubbleMarker.label = photo.title;
map.markerLayer.addOverlay( bubbleMarker );
var image : Image = new Image();
// http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg
image.source = "http://farm" + photo.farm +
".static.flickr.com/" + photo.server + "/" + photo.id + "_" + photo.secret + "_t.jpg";
bubbleMarker.element = image;
}
]]>
</mx:Script>
<framework:Framework apiKey="19640523"/>
<mx:Panel width="100%" height="100%">
<map:Map id="map"/>
<mx:ControlBar>
<mx:Button label="Search Photos" click="searchPhotos()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
And here is the Photo class
package com.esri.flickr
{
public class Photo
{
public var id : String;
public var owner : String;
public var secret : String;
public var server : String;
public var farm : String;
public var title : String;
public var isPublic : Boolean;
public var isFriend : Boolean;
public var isFamily : Boolean;
public var latitude : Number;
public var longitude : Number;
public var accuracy : int;
}
}
Wednesday, October 17, 2007
Flex and OSGi
The AWX framework is based on a simplified OSGi implementation in AS3. The advent of flex modules made this implementation possible. The following is a simple walk-through that showcases how to use this solution. The idea is to define an interface whose implementation can be defined and discovered at runtime. Using FlexBuilder as a project manager makes things a bit easier.
Create a flex library project named "FooAPI" to define the following interface. A SWC "FooAPI.swc" is generated upon the build in the "bin" directory.
Create the main application as a flex project.
The
The
The
The
Create a flex library project named "FooAPI" to define the following interface. A SWC "FooAPI.swc" is generated upon the build in the "bin" directory.
package com.esri.awxCreate a flex actionscript project named "FooBundle" - make sure to add the AWX swc as a project library and the "FooAPI" project. Modify the FooBundle.as class as follows:
{
public interface IFoo
{
function doFoo( text : String ) : String;
}
}
package {Here we are creating a flex module that implements the
import com.esri.aws.osgi.framework.IBundleActivator;
import com.esri.aws.osgi.framework.IBundleContext;
import com.esri.awx.FooImpl;
import com.esri.awx.IFoo;
import flash.utils.getQualifiedClassName;
import mx.modules.ModuleBase;
public class FooBundle extends ModuleBase implements IBundleActivator
{
public function start( context : IBundleContext ) : void
{
context.registerService( getQualifiedClassName(IFoo), new FooImpl());
}
public function stop( context : IBundleContext ) : void
{
}
}
}
IBundleActivator
interface that defines the start
and stop
functions. In the start
fnction implementation, we register a FooImpl instance with the framework. The value of getQualifiedClassName(IFoo) (Notice, we are using the IFoo interface) is acting as a key, that we can use in other bundles to get a reference to the IFoo implementation.package com.esri.awxThe compilation of this project will output a FooBundle.swf file. This is the bundle (in OSGi parlant) that we will load dynamically at runtime. Make sure that you set the output of the project to a web application. This will facilitate the following step.
{
public class FooImpl implements IFoo
{
public function doFoo( text : String ) : String
{
return "foo::"+text;
}
}
}
Create the main application as a flex project.
<?xml version="1.0" encoding="utf-8"?>Here in the main application, we create an instance of the
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:awx="http://www.arcwebservices.com/2007/awx"
layout="horizontal"
>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import com.esri.awx.IFoo;
import flash.utils.getQualifiedClassName;
import com.esri.aws.osgi.framework.IServiceReference;
import com.esri.aws.osgi.framework.IBundle;
import com.esri.aws.osgi.framework.IBundleContext;
private function listBundles() : void
{
trace( "---Bundles---");
var context : IBundleContext = framework.systemContext;
for each ( var bundle : IBundle in context.getBundles())
{
trace( bundle.getBundleID()+" "+bundle.getSymbolicName());
}
}
private function loadFoo() : void
{
framework.installBundle("http://host/webapp/FooBundle.swf");
}
private function doFoo() : void
{
var context : IBundleContext = framework.systemContext;
var ref:IServiceReference = context.getServiceReference(getQualifiedClassName(IFoo));
if( ref == null)
{
Alert.show( "No reference to service IFoo");
}
else
{
var foo : IFoo = context.getService( ref ) as IFoo;
trace( foo.doFoo( "Hello"));
}
}
private function uploadFoo() : void
{
var bundleID : int = -1;
var context : IBundleContext = framework.systemContext;
for each ( var bundle : IBundle in context.getBundles())
{
if( bundle.getLocation() == "http://host/webapp/FooBundle.swf")
{
bundleID = bundle.getBundleID();
break;
}
}
if( bundleID != -1)
{
context.getBundle(bundleID).uninstall();
}
}
]]>
</mx:Script>
<mx:TraceTarget/>
<awx:Framework id="framework" apiKey="xxxxxxx"/>
<mx:Button label="List Bundles" click="listBundles()"/>
<mx:Button label="Load Foo" click="loadFoo()"/>
<mx:Button label="Do Foo" click="doFoo()"/>
<mx:Button label="Upload Foo" click="uploadFoo()"/>
</mx:Application>
Framework
.The
listBundles
function lists all the available bundles. By default, the framework installs the system bundle and an authentication bundle.The
loadFoo
function installs the foo bundle from a url.The
unloadFoo
function unloads the bundle from the framework, which automagically unregisters any registered services.The
doFoo
function demonstrates how to get a reference to an IFoo implementation. If the reference is null
then the service was not loaded or was unloaded. if we have a non-null reference value, then we use it to get a service reference, that we can safely cast to IFoo and act upon it.Tuesday, October 16, 2007
Hey ArcGIS users
You can create an ArcGIS Server based layer using the AWX Flex API. In the below sample, I'm loading a layer from a specified endpoint and then when I click click on the button, I'm turning off the "water" layer in the ArcGIS group layer.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.ItemResponder;
import com.esri.aws.awx.map.layers.LayerVisibility;
private function turnWaterOff() : void
{
ags.getLayerVisibilities( new ItemResponder(
function(data : Object, token : Object = null) : void {
var arrCol : ArrayCollection = data as ArrayCollection;
for each ( var layerVisibility : LayerVisibility in arrCol)
{
if( layerVisibility.name == "Water")
{
layerVisibility.visible = false;
}
}
},
function(info : Object, token : Object = null) : void {
trace( info );
}
));
}
]]>
</mx:Script>
<framework:Framework apiKey="196405023"/>
<mx:Panel width="100%" height="100%">
<map:Map>
<map:basemaps>
<layers:ArcGISMapServerGroupLayer id="ags"
endpointURI="http://host/arcgis/services/MapService/MapServer"
/>
</map:basemaps>
</map:Map>
<mx:ControlBar>
<mx:Button label="Turn Water Off" click="turnWaterOff()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
Monday, October 15, 2007
Wednesday, October 10, 2007
Tuesday, October 2, 2007
My MAX 2007 Presentation
Thanks all for attending, it was a packed room. You can download a zipped ppt version of my presentation from here.
Wednesday, September 26, 2007
Trace AWX errors
We use Flex logging capabilities to report debug, info and error information:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
layout="absolute"
>
<mx:TraceTarget/>
<framework:Framework
username="foo"
password="bar"
authenticationError="trace(event.getError())"/>
</mx:Application>
Tuesday, September 25, 2007
I get a blank map first time through...
If you are using Flex Builder
, make sure that you set the output folder of the project to a web application folder. Remember, your api key is tied to a URL. To get to this setting, use File -> Properties -> Flex Build Path.
WMS basemap layer
You can subclass BaseMapLayer to create a WMS basemap layer:
Here is how you use it in MXML:
package com.esri.aws.awx.map.layers
{
import com.esri.aws.awx.geom.Extent;
import flash.geom.Point;
import flash.utils.getTimer;
public class WMSGroupLayer extends BaseMapLayer
{
public var service : String = "WMS";
public var version : String = "1.1.1";
public var request : String = "GetMap";
public var format : String = "image/png";
public var wmsLayers : String;
public var serviceName : String;
public var wmtVersion : String;
public var styles : String;
public var srs : String;
public var dataSource:String;
public var proxyURL : String;
public function WMSGroupLayer()
{
super();
mapOversizeBuffer = 0;
}
override protected function getMap():void
{
m_lastMap = buildGeoLoader();
var scalarPt:Point = getScalar();
var width:Number = Math.floor(map.rotatedWidth * scalarPt.x);
var height:Number = Math.floor(map.rotatedHeight * scalarPt.y);
m_lastMap.startTime = getTimer();
m_lastMap.mapHeight = height;
m_lastMap.mapWidth = width;
m_lastMap.center = map.center;
m_lastMap.mapRotation = map.mapRotation;
m_lastMap.scale = map.scale;
m_lastMap.projectionID = map.projectionID;
var extent : Extent = map.extent;
var index : int = dataSource.indexOf( "?");
var prefix : String = index == -1 ? "?" : "&";
var url : String = dataSource;
url += prefix + "SERVICE="+service;
url += "&VERSION="+version;
url += "&REQUEST="+request;
if( serviceName != null)
{
url += "&SERVICENAME="+serviceName;
}
if( wmtVersion != null)
{
url += "&WMTVER="+wmtVersion;
}
if( wmsLayers != null)
{
url += "&LAYERS="+wmsLayers;
}
if( styles != null)
{
url += "&STYLES="+styles;
}
if( srs != null)
{
url += "&SRS="+srs;
}
url += "&FORMAT="+format;
url += "&WIDTH="+width;
url += "&HEIGHT="+height;
url += "&BBOX="+extent.minX+","+extent.minY+","+extent.maxX+","+extent.maxY;
if( proxyURL != null)
{
url = proxyURL + "?" + escape( url);
}
// trace( url );
centerMap(m_lastMap);
m_lastMap.load( url);
}
}
}
Here is how you use it in MXML:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
layout="absolute"
>
<framework:Framework apiKey="XXXXXX"/>
<map:Map id="map" centerLat="40.708512143644064" centerLon="-74.01304349200724" scale="200000">
<map:basemaps>
<layers:WMSGroupLayer
dataSource="http://gisdata.usgs.net/servlet/com.esri.wms.Esrimap"
serviceName="USGS_WMS_LANDSAT7"
wmtVersion="1.1.0"
wmsLayers="LANDSAT_LZ77"
srs="EPSG:4326"
/>
</map:basemaps>
</map:Map>
</mx:Application>
Friday, September 21, 2007
Presenting at Adobe MAX 2007
Will be in Chicago at Adobe MAX 2007, and presenting on:
Hope to see you there.
- Monday, October 1 4:30 pm - 5:30 pm
- Tuesday, October 2 9:15 am - 10:15 am
Hope to see you there.
Thursday, September 20, 2007
Current US precipitation
You can take advantage of AWS data sources, in this case Precipitation.US, to view dynamic data.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
xmlns:services="com.esri.aws.services.*"
layout="absolute"
>
<framework:Framework apiKey="19640523">
<services:MapImageActivator/>
</framework:Framework>
<map:Map centerLat="35" centerLon="-93" scale="20000000">
<map:basemaps>
<layers:MapImageGroupLayer dataSource="ArcWeb:MX.Precipitation.US">
<layers:layerVisibilities>
<layers:LayerVisibility name="U.S. States" visible="false"/>
</layers:layerVisibilities>
</layers:MapImageGroupLayer>
</map:basemaps>
</map:Map>
</mx:Application>
AWX2 beta users count keeps growing
As of today, the number of AWX2 beta users have reached a cool 883 !
Tuesday, September 18, 2007
Hey ArcIMS users....
You can use an ArcIMS datasource as a basemap, as follows:
Don't forget to place a crossdomain.xml file at the base of your ArcIMS host. Check out the Geography Network cross domain file as a sample.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
layout="absolute"
>
<framework:Framework apiKey="19640523"/>
<map:Map>
<map:basemaps>
<layers:ArcIMSGroupLayer id="world"
serviceHost="http://www.geographynetwork.com"
serviceName="ESRI_World"
/>
</map:basemaps>
</map:Map>
</mx:Application>
Don't forget to place a crossdomain.xml file at the base of your ArcIMS host. Check out the Geography Network cross domain file as a sample.
Monday, September 17, 2007
How to create a dashed line ?
This code is based on the great work from Eli. You can download all the source code from here. So... in mxml it will look like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
xmlns:style="com.esri.aws.awx.map.layers.overlays.style.*"
xmlns:overlays="com.esri.aws.awx.map.layers.overlays.*"
xmlns:geom="flash.geom.*"
>
<framework:Framework/>
<map:Map>
<map:basemaps>
<layers:PolylineLayer>
<layers:style>
<style:StrokeStyle>
<style:stroke>
<mx:Stroke color="0xFF0000" alpha="0.5" weight="5" caps="round"/>
</style:stroke>
<style:pattern>
<mx:Number>10</mx:Number>
<mx:Number>10</mx:Number>
</style:pattern>
</style:StrokeStyle>
</layers:style>
<overlays:Polyline>
<overlays:coords>
<geom:Point y="0" x="0"/>
<geom:Point y="45" x="-90"/>
<geom:Point y="-45" x="-90"/>
</overlays:coords>
</overlays:Polyline>
</layers:PolylineLayer>
</map:basemaps>
</map:Map>
</mx:Application>
Thursday, September 13, 2007
Map distance measuring tool
Following the previous post, here is an example on how you will be able to use in the upcoming beta3 the polyline mouse handler to create a distance measuring tool.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:utils="com.esri.aws.awx.utils.*"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import com.esri.aws.awx.utils.Utils;
import com.esri.aws.awx.map.layers.overlays.Polyline;
import com.esri.aws.awx.events.OverlayEvent;
import com.esri.aws.awx.map.handlers.PolylineMouseHandler;
import com.esri.aws.awx.map.handlers.IMouseHandler;
private var m_mouseHandler : IMouseHandler;
private function doMeasure() : void
{
m_mouseHandler = map.mouseHandler;
var mouseHandler : PolylineMouseHandler = new PolylineMouseHandler( map.polylineLayer);
mouseHandler.addEventListener(OverlayEvent.OVERLAY_CHANGED, function(event : OverlayEvent) : void
{
var polyline : Polyline = event.overlayObject as Polyline;
var coords : Array = polyline.coords;
var td : Number = 0.0;
for( var i:int = 0, j:int=1; j<coords.length; i++, j++)
{
var p1 : Point = coords[i] as Point;
var p2 : Point = coords[j] as Point;
td += Utils.distVincenty(p1.y, p1.x, p2.y, p2.x);
}
distance.text = td.toFixed(2);
});
mouseHandler.addEventListener(OverlayEvent.OVERLAY_CREATED, function(event : OverlayEvent) : void
{
map.mouseHandler = m_mouseHandler;
});
map.mouseHandler = mouseHandler;
distance.text = "0";
}
]]>
</mx:Script>
<framework:Framework apiKey="19640523"/>
<mx:Panel title="Measure Tool" width="100%" height="100%">
<map:Map id="map"/>
<mx:ControlBar>
<mx:Button label="Measure" click="doMeasure()"/>
<mx:Button label="Clear" click="map.polylineLayer.removeAllOverlays()"/>
<mx:Label text="Meters:"/>
<mx:Label id="distance" text="0"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
Distance between two Latitude/Longitude points
So when I asked my friend Kerry (who BTW is the king when it comes to map projections) how to calculate the distance between two lat/lon points ? he pointed me to this link. He followed by saying "This is a good start, but what I have implemented in Java, takes care of all the 'weird' conditions!". This is why he is the king :-). So you can find the AS3 implementation here. And here is a simple MXML calculator.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import com.esri.aws.awx.utils.Utils;
private function onClick() : void
{
var lat1n : Number = Utils.parseDeg(lat1.text);
var lon1n : Number = Utils.parseDeg(lon1.text);
var lat2n : Number = Utils.parseDeg(lat2.text);
var lon2n : Number = Utils.parseDeg(lon2.text);
var dist : Number = Utils.distVincenty( lat1n, lon1n, lat2n, lon2n);
distLabel.text = dist.toFixed(2) + " meter(s)"
}
]]>
</mx:Script>
<mx:Form width="100%" height="100%">
<mx:FormHeading label="Geodesic Distance Calculator"/>
<mx:FormItem label="lat 1:">
<mx:TextInput id="lat1" width="200" text="53 09 02N"/>
</mx:FormItem>
<mx:FormItem label="lon 1:">
<mx:TextInput id="lon1" width="200" text="001 50 40W"/>
</mx:FormItem>
<mx:FormItem label="lat 2:">
<mx:TextInput id="lat2" width="200" text="52 12 19N"/>
</mx:FormItem>
<mx:FormItem label="lon 2:">
<mx:TextInput id="lon2" width="200" text="000 08 33W"/>
</mx:FormItem>
<mx:FormItem label="dist">
<mx:Label id="distLabel"/>
</mx:FormItem>
<mx:FormItem>
<mx:Button
label="Calculate"
click="onClick()"/>
</mx:FormItem>
</mx:Form>
</mx:Application>
Wednesday, September 12, 2007
LiveHTTPHeaders
Following the last post, AWX can connect directly to a resource such as ArcIMS or even ArcGIS to get maps. The best way that I found out to see what crossdomain.xml the player is requesting, is to run the application in Firefox and watch the HTTP traffic using LiveHTTPHeaders.
Tuesday, September 11, 2007
I do not get a map...
Make sure that you have a crossdomain.xml file at the base of you web server that is hosting ArcIMS, ArcGIS or any host that you are trying to access data from that is not the host from which the swf was downloaded from. In such a way, that if you type http://your-host-name-info/crossdomain.xml in a web browser, you should get:
If you cannot create that file on that host, then use/create a proxy with a crossdomain file.
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" secure="false"/>
</cross-domain-policy>
If you cannot create that file on that host, then use/create a proxy with a crossdomain file.
How to turn off layer visibility ?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import com.esri.aws.awx.map.layers.LayerVisibility;
private function onClick() : void
{
world.layerVisibilities = [
new LayerVisibility("Major Rivers", false)
];
}
]]>
</mx:Script>
<framework:Framework apiKey="19640523"/>
<mx:Panel width="100%" height="100%">
<map:Map>
<map:basemaps>
<layers:ArcIMSGroupLayer id="world"
serviceHost="http://www.geographynetwork.com"
serviceName="ESRI_World"
autoLoadLayerVisibilities="true"
/>
</map:basemaps>
</map:Map>
<mx:ControlBar>
<mx:Button label="Turn Off Major Rivers" click="onClick()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
How to add a polygon to the map ?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:map="com.esri.aws.awx.map.*"
creationComplete="onCreationComplete()"
layout="absolute"
>
<mx:Script>
<![CDATA[
import flash.geom.Point;
import com.esri.aws.awx.map.layers.overlays.Polygon;
import com.esri.aws.awx.geom.PolygonShape;
private function onCreationComplete() : void
{
var coords : Array = [
new Point( 0, 0),
new Point( -97.06138,32.837),
new Point( 0, 32.832),
new Point( 0, 0)
];
var polygonShape : PolygonShape = new PolygonShape( coords);
var polygon : Polygon = new Polygon( polygonShape);
map.polygonLayer.addOverlay( polygon);
}
]]>
</mx:Script>
<framework:Framework/>
<map:Map id="map"/>
</mx:Application>
Wednesday, September 5, 2007
How to create geographic circles ?
In the current beta-2 version of AWX, you can create geographical circles using the Circle and CircleShape classes. In the beta-3 version, you will be able to do:
<map:Map>
<map:basemaps>
<layers:PolygonLayer>
<overlays:Circle centerLat="0" centerLon="0" radius="500000" units="meters"/>
<overlays:Circle centerLat="45" centerLon="-80" radius="500" units="kilometers"/>
<overlays:Circle centerLat="60" centerLon="80" radius="500" units="miles"/>
</layers:PolygonLayer>
</map:basemaps>
</map:Map>
Framework start event
Wait for the framework start event to lookup services registered by the activator.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:services="com.esri.aws.services.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import com.esri.aws.services.IRouteFinder;
import flash.utils.getQualifiedClassName;
import com.esri.aws.osgi.framework.IServiceReference;
import com.esri.aws.osgi.framework.Framework;
import com.esri.aws.osgi.framework.IBundleContext;
private function frameworkStartHandler() : void
{
var context : IBundleContext = Framework.getInstance().systemContext;
var ref : IServiceReference = context.getServiceReference( getQualifiedClassName(IRouteFinder));
if( ref != null)
{
var routeFinder : IRouteFinder = context.getService( ref) as IRouteFinder;
trace( routeFinder);
}
else
{
Alert.show( "No IRouteFinder Reference");
}
}
]]>
</mx:Script>
<framework:Framework apiKey="19640523" frameworkStart="frameworkStartHandler()">
<services:RouteFinderActivator/>
</framework:Framework>
</mx:Application>
How to reverse geocode a location ?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:services="com.esri.aws.services.*"
xmlns:awx="com.esri.aws.awx.*"
implements="mx.rpc.IResponder"
initialize="initializeHandler()"
layout="absolute">
<mx:Script><![CDATA[
import mx.logging.targets.TraceTarget;
import mx.logging.Log;
import com.esri.aws.services.GeocodeCandidate;
import com.esri.aws.awx.map.handlers.ReverseGeocodeMouseHandler;
import com.esri.aws.services.FindActivator;
import com.esri.aws.services.GeocodeInfo;
import com.esri.awx.RevGeoMouseHandler;
private function initializeHandler() : void
{
map.mouseHandler = new ReverseGeocodeMouseHandler(this);
}
public function result(data:Object):void
{
var geocodeInfo:GeocodeInfo = data as GeocodeInfo;
for each ( var candidate : GeocodeCandidate in geocodeInfo.candidates)
{
trace( candidate.desc1 );
}
}
public function fault(info:Object):void
{
trace( info);
}
]]></mx:Script>
<framework:Framework apiKey="19640523">
<services:FindActivator/>
</framework:Framework>
<map:Map id="map" centerLon="-96.8577" centerLat="38.9762" scale="26000000"/>
</mx:Application>
Wednesday, August 29, 2007
How to get a layer visibility info ?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:awx="com.esri.aws.awx.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:services="com.esri.aws.services.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import com.esri.aws.awx.map.layers.LayerVisibility;
import mx.collections.ItemResponder;
private function onFrameworkStart() : void
{
parcels.getLayerVisibilities( new ItemResponder(
function(data:Object, token:Object = null):void
{
for each ( var layerVisibility : LayerVisibility in data )
{
trace( layerVisibility.name + " " + layerVisibility.visible);
}
},
function(info:Object, token:Object = null):void
{
trace(info);
}
));
}
]]>
</mx:Script>
<framework:Framework
apiKey="19640523"
frameworkStart="onFrameworkStart()"/>
<map:Map>
<map:basemaps>
<layers:VectorGroupLayer id="parcels"
dataSource="ArcWeb:BR.Parcels.US"
autoLoadLayerVisibilities="false"/>
</map:basemaps>
</map:Map>
</mx:Application>
Friday, August 24, 2007
BTW...Hello and welcome to my blog
My name is Mansour Raad. I'm the senior software architect for ESRI ArcWeb Services. In addition, I'm the resident Adobe Flex evangelist. So...this series of blogs is about tips and tricks on how to use ArcWebExplorer (in beta as of this writing). you can download the swc and check out samples from http://www.arcwebservices.com/v2006/labs/awx2_lab.do.
Have fun and stay tuned.
Have fun and stay tuned.
How to get parcel information ?
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:framework="com.esri.aws.osgi.framework.*"
xmlns:services="com.esri.aws.services.*"
xmlns:map="com.esri.aws.awx.map.*"
xmlns:layers="com.esri.aws.awx.map.layers.*"
layout="absolute"
>
<mx:Script>
<![CDATA[
import com.esri.aws.services.SpatialQueryOptions;
import com.esri.aws.services.ISpatialQuery;
import com.esri.aws.services.PlaceFinderOptions;
import mx.collections.ItemResponder;
import com.esri.aws.services.IPlaceFinder;
import flash.utils.getQualifiedClassName;
import com.esri.aws.osgi.framework.IServiceReference;
private function doSpatialQuery() : void
{
var ref : IServiceReference = framework.systemContext.getServiceReference(getQualifiedClassName(ISpatialQuery));
var spatialQuery : ISpatialQuery = framework.systemContext.getService( ref ) as ISpatialQuery;
var point : Point = new Point(map.centerLon, map.centerLat);
var spatialQueryOptions : SpatialQueryOptions = new SpatialQueryOptions();
spatialQueryOptions.dataSource = "ArcWeb:BR.DetailedParcels.CA";
spatialQuery.findFeaturesByPoint( point, spatialQueryOptions, new ItemResponder(
function( data : Object, token : Object = null ) : void
{
trace( data );
},
function( info : Object, token : Object = null ) : void
{
trace( info );
}
));
}
]]>
</mx:Script>
<framework:Framework id="framework" apiKey="19640523">
<services:SpatialQueryActivator/>
</framework:Framework>
<mx:Panel width="100%" height="100%">
<map:Map id="map">
<map:basemaps>
<layers:VectorGroupLayer id="parcels" dataSource="ArcWeb:BR.Parcels.US" autoLoadLayerVisibilities="false"/>
</map:basemaps>
</map:Map>
<mx:ControlBar>
<mx:Button label="Find Parcel" click="doSpatialQuery()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>
Subscribe to:
Posts (Atom)