Monday, December 22, 2008

Flaring Clustered Map Points

Following the last post, It would be nice to inspect the clustered set of map points when say the set is less than a dozen or so. In this post, I'm demonstrating something I call a FlareSymbol, which is a Symbol sub-class, where like before, the number of clustered points is shown. But, when you click on the cluster, it flares out to show the individual map points. Each flared point can react to say a mouse click to popup an info window showing its attribute values. When you click again on the cluster, it collapses the map points back to its center. Here it is in action. And like usual, you can download the source from here.

Friday, December 19, 2008

Clustering 20K+ Map Points

Was asked "what is the best way to display 20,000 markers on a map ?". Well, displaying all 20K map points does not make whole lot of sense, as you just see nothing, just a blanket of markers. A smarter approach is to cluster the points and display the clusters. Somebody pointed me to mapeed, and asked me to duplicate this using the Flex API for AGS. Here it is. I've loaded 20,000 map points from geonames and created dynamically clusters based on the scale of the map and its extent. BTW, this is all client side processing - gotta love the JIT in Flash Player :-)
[Update] Sooo many people asked me for the source code of the app - here it is. Any feedback is more than welcome.

Sunday, December 14, 2008

Editing Graphic Geometry

In this post, I'm demonstrating how to move a vertex of a polygon to modify its geometry. The application displays a graphic with a simple fill symbol. When the user clicks the Start Edit button, the symbol of the graphic is assigned to a new custom symbol instance, where the outline of the polygon is rendered as a line and the vertexes as little squares. While holding the mouse down over a square, a user can move the vertex to a new location on the map. The user can repeat this process over other vertexes until the Stop Edit button is clicked. You can see the application in action here. And like usual, the source is available from here.

Monday, December 1, 2008

Geolocation using Gears and Geode

Location based Services applications are requiring more and more that the geographical coordinates (or at least the neighborhood) of the user be automatically detected to create a more seamless and streamlined experience. Google Gears with its geolocation API and Mozilla Labs with its Geode project are making the W3C geolocation specification a reality. With such democratization of services, it would be nice to have geolocation as a native function in the flash player, so would be a JSON parser (but that is a topic of conversation for another day). Until then, we have to rely on the browser's capabilities to feed that information into the player. This can easily achieved using the ExternalInterface class. In this post, I adopted the geolocation W3C specification and implemented an ActionScript class to wrap the Gears and Geode javascript functions so they can be invoked from a Flex application. Make sure to install Gears and/or Geode to test this application. Using FlexBuilder, the index.template.html has to be adjusted to include the gears_init.js file and the geolocation.js wrapper file as follows:

...
<script type="text/javascript" src="AC_OETags.js"></script>
<script type="text/javascript" src="gears_init.js"></script>
<script type="text/javascript" src="geolocation.js"></script>
<style>
...

The registration of the callback functions is done by creating an instance of the GeoLocation class:

...
<geolocation:GeoLocation id="geolocation"/>
...

The position of the web application can be determined using something of the form:

...
geolocation.getCurrentPosition( currentPositionSuccessHandler, currentPositionFaultHandler );
...

Check out the application here. And like usual, you can download the source from here.

Shapefile Viewer

Due to the ubiquitous nature of geographical data in shapefile format, I want to embed a shapefile in a flex application and display it as a layer in a map.
This work is borrowed and modified from Edwin van Rijkom. The modifications include the generation of Geometry subclasses based on the Flex API for ArcGIS Server.
So, to get started, I created a ShpLayer class that is a subclass of GraphicsLayer. I initialized this layer's symbol property with an instance of ShpSymbol which is my own custom symbol (more on this later). This way, I do not have to assign a symbol to each added Graphic instance. Internally, when a graphic is rendered, and that graphic has a null symbol property, it inherits its parent's symbol property value. This is an easy way to globally change the symbology of a graphic layer, without having to explicitly iterate through all its children :-)
The fact that I've created my own custom symbol is yet another demonstration of the extensibility of the API to render a Graphic in any which way I seem fit. This was easily done by overriding the draw public function.
In this example, I'm filling and outlining a polygon. In addition, I'm annotating the polygon with its label property and locating that annotation at its label X/Y property values.
The embedding of the shapefile (shp and dbf) in the application is accomplished by the following statement (Note the mimeType):

[Embed(source="assets/ContinentsWithLabels.shp",mimeType="application/octet-stream")]
private var m_shpClass : Class;

[Embed(source="assets/ContinentsWithLabels.dbf",mimeType="application/octet-stream")]
private var m_dbfClass : Class;

I've overridden the createChildren protected function to load into a byte array the shp and dbf content from which I created a ShpReader and a DbfHeader. I then iterated over each feature to create a Graphic instance which I pushed onto an array. At the end of the iteration, the array was converted to an ArrayCollection that became the layer's graphic provider. Here is the application in action, and like usual you can download the source from here.
One more gem. Note how I created my own subclass of Map, where I've overridden the updateDisplayList protected function to create a nice gradient filled background.

Sunday, November 23, 2008

Sunday, November 16, 2008

Polygon and Geodetic Circle Draw Tool

In this sample, I'm demonstrating how to roll you own draw tool using the Flex API for ArcGISServer. I'm attaching mouse listeners to the map and based on the selected tool, I'm drawing a polyline, a circle or a geodetic circle. For the latter, I'm using the undocumented (subject to changes) ProjUtils class to enable me to calculate the distance in meters between two lat/lon values. In addition, this class has the capability to calculate a lat/lon value for a given lat/lon origin, radius and azimuth. When using this sample, watch out for boundary condition (too close to the poles or crossing the date line) as this is a very simple implementation. How to handle these conditions will be another post :-) as usual, you can download the source from here. Have fun, and hope to see some of you at MAX next week.

Tuesday, November 11, 2008

Serializing Geometries To BlazeDS

Was asked if there is a way to serialize the geometries of a Flex client application using for example BlazeDS. And the answer is 'yes'. All geometries are tagged with a [RemoteClass] metadata. Here is the break down (BTW - this is subject to change in the next version):

SpatialReference com.esri.ags.ASSpatialReference
MapPoint com.esri.ags.geometry.ASPoint
Polyline com.esri.ags.geometry.ASPolyline
Polygon com.esri.ags.geometry.ASPolygon

Here is for example the Java side of MapPoint:

package com.esri.ags.geometry;

import com.esri.ags.ASSpatialReference;

public class ASPoint implements IASGeometry
{
public double x;
public double y;
public ASSpatialReference spatialReference;
}

The following is a very simple client application that calls a RemoteObject to save a MapPoint instance:

<?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.ags.geometry.MapPoint;
private function clickHandler() : void
{
mapPointRO.save( new MapPoint( 45, 45 ));
}
]]>
</mx:Script>
<mx:RemoteObject id="mapPointRO" destination="mapPoint"/>
<mx:Button label="Save" click="clickHandler()"/>
</mx:Application>

The RemoteObject destination is defined in remoting-config.xml:

<destination id="mapPoint">
<properties>
<source>com.esri.remoteobject.MapPointRO</source>
<scope>application</scope>
</properties>
</destination>

And here is the POJO under BlazeDS:

package com.esri.remoteobject;

import com.esri.ags.geometry.ASPoint;

public class MapPointRO
{
public void save(final ASPoint asPoint)
{
System.out.println("asPoint.x = " + asPoint.x);
System.out.println("asPoint.y = " + asPoint.y);
}
}

You can download all the Java implementations from here.

Friday, November 7, 2008

Speaking At MAX 2008

This year, I will be a speaker at MAX 2008. The session title is 'Delivery of a Mission-Critical RIA for NATO' and is held on Tuesday at 4:30. Hope to see you all there.

Saving Map Snapshot

This week, while in Redlands for a holistic lab session on the Flex API for ArcGIS Server, a user showed me the following gem. Using the new Flash Player 10 FileReference class, you can create an image snapshot of a map component and save it locally to a file, without a server side service. To compile the application using FlexBuilder, you need to adjust your compiler SDK to version 3.2.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
layout="vertical"
>
<mx:Script>
<![CDATA[
import mx.graphics.ImageSnapshot;
import mx.graphics.codec.JPEGEncoder;

private function clickHandler() : void
{
const decoder: JPEGEncoder = new JPEGEncoder();
map.logoVisible=false;
map.scaleBarVisible=false;
map.zoomSliderVisible = false;
const imageSnapshot:ImageSnapshot = ImageSnapshot.captureImage(map,96,decoder,true);
map.logoVisible=true;
map.scaleBarVisible=true;
map.zoomSliderVisible = true;
const fileReference:FileReference = new FileReference();
fileReference.save(imageSnapshot.data,"map.jpg");
}
]]>
</mx:Script>
<esri:Map id="map">
<esri:ArcGISTiledMapServiceLayer
url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
</esri:Map>
<mx:Button label="Snapshot" click="clickHandler()"/>
</mx:Application>

Thanks Ofir :-)

Monday, November 3, 2008

Find Nearby Place

So... if you are given a latitude and a longitude values and you want to find a nearby place, then you have to check out GeoNames. They have a web API that you can access for free (as in beer :-) Here is a simple usage of the API using the Flex API for AGS:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
layout="absolute"
>
<mx:Script>
<![CDATA[
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.Graphic;
import com.esri.ags.events.MapMouseEvent;
import mx.rpc.events.ResultEvent;

private function mapClickHandler( event : MapMouseEvent ) : void
{
const urlVar : URLVariables = new URLVariables();
urlVar.lat = event.mapPoint.y.toFixed( 6 );
urlVar.lng = event.mapPoint.x.toFixed( 6 );
httpService.send( urlVar );
}

private function resultHandler( event : ResultEvent ) : void
{
const geonames : XML = event.result as XML;
const geoname : XML = geonames.geoname[0];
const name : String = geoname.name;
const lat : Number = geoname.lat;
const lng : Number = geoname.lng;
const graphic : Graphic = new Graphic( new MapPoint( lng, lat));
graphic.toolTip = name;
gl.add( graphic );
}
]]>
</mx:Script>
<mx:HTTPService id="httpService"
useProxy="false"
resultFormat="e4x"
url="http://ws.geonames.org/findNearbyPlaceName"
result="resultHandler(event)"/>
<esri:Map
openHandCursorVisible="false"
mapClick="mapClickHandler(event)">
<esri:ArcGISTiledMapServiceLayer
url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
<esri:GraphicsLayer id="gl">
<esri:symbol>
<esri:SimpleMarkerSymbol color="0xFF0000"/>
</esri:symbol>
</esri:GraphicsLayer>
</esri:Map>
</mx:Application>

Sunday, October 26, 2008

Unit Testing with Fluint

If you like FlexUnit, then you really wanna check out Fluint. It does the basic unit test assert stuff, but my favorite is the sequence testing. This is where it blows FlexUnit out of the water. Great job Mike, Jeff and Mike, you have a new convert !

Saturday, October 25, 2008

Module Not Loading...WT(bleep)

Flex modules are very cool. In the AWS/X days, wrote a whole OSGi-like framework based on modules. So for this new project that I'm working on, I decided to reuse them again. Wrote a simple test case to remind myself how to use them with new Flex Builder, which BTW makes building modular application really easy. No more link reports CLI (well...it does it for you internally :-). So in my smart way (NOT) wrote the following:

public function loadModule() : void
{
const info : IModuleInfo = ModuleManager.getModule( "TestModule.swf");
info.addEventListener( ModuleEvent.READY, readyHandler );
info.load(ApplicationDomain.currentDomain);
}

private function readyHandler( event : ModuleEvent ) : void
{
trace( event.type );
}

Whenever I invoked the loadModule() function at the application creation complete, no trace would appear in my console. WT(bleep). Kept looking at the code, and re-launching the application, assuming that this time it will do it (come on, you've done that too, knowing perfectly well that the outcome will be the same ;-) After a dozen time (yes, I'm that stupid) and blaming the new Flash Player 10, I decided to read the documentation, and came across this:

"Be sure to define the module instance outside of a function, so that it is not in the function's local scope. Otherwise, the object might be garbage collected and the associated event listeners might never be invoked."

Retard !!!

Readjusted the code, as follows:

private var m_info : IModuleInfo;

public function loadModule() : void
{
m_info = ModuleManager.getModule( "TestModule.swf");
m_info.addEventListener( ModuleEvent.READY, readyHandler );
m_info.load(ApplicationDomain.currentDomain);
}

And guess what ? it works! After more reading, found out that the former code was a great way to pre-load modules. Anyway, hope you find this useful and funny. And remember: When all fails, RTFM. Or should it just be RTFM - LOL!

Thursday, October 23, 2008

[ANN] Flex Mapping API

I'm pleased to announce that we just released the Flex API for ArcGIS Server. Check it out here, and make sure to tell us what you think. Good or bad. I'm already on the road preaching the cool stuff you can do with it :-). BTW, it works on Flash Player 10 (though this is not "officially" supported), taking advantage of the speedier JIT and AVM. Check out the samples and the associated source codes to see how it works in MXML and AS3. Happy Mapping All :-)

Sunday, October 12, 2008

Faster JSON Decoder - NOT :-(

I'm looking for a fast JSON decoder in AS3, and I'm really hoping that the next Flash player will have native JSON functions like IE8. The as3 core library has a decoder. Tested it on a one megabyte JSON String and it took almost 4 seconds to decode on my MacBook Pro. Wow ! There has to be a better way. Couple of former life ago, was really into lex/yacc and flex/bison. BTW - I laughed so hard when I read the name of the first link when I googled 'lex yacc' - The generated "C" code was very fast. So one of the "modern" way to write a lexical analyzer/parser is to use ANTLR. ANTLR has an option to target ActionScript. I Found a JSON grammar reference and made simple adjustments for AS3. Ran the ANTLR Tool on the 'g' files and compiled the generated AS files into a test harness. BTW - Had to adjust by hand the generated AS3 code, as it did not compile out-of-the-box :-( I passed a set of test cases for the exception of escaped string content. Now, back to the one megabyte JSON String. It took almost 29 seconds !!!! Wow ! Thinking about it some more. It kinda make sense, as I'm evaluating the string from a generated AST. Anyway, I believe you should "publish" your successes and failures. Like usual, here is the source code.

Friday, October 3, 2008

REST + JSON + AMF

So...I was asked to investigate what will it take to output data in AMF to an existing REST endpoint. The idea is that, with just a path info switch, the endpoint can output XML, JSON or AMF i.e. http://server/rest/service/json, http://server/rest/service/amf. And no, a direct BlazeDS integration is not an option :-( So after looking at the BlazeDS source code (you gotta love open source :-) I homed in on the Java15Amf3Output class. It take an output stream as a property and has a writeObject method. Cool ! So given an Address instance:

package com.esri.webamf;

import java.io.Serializable;

public class Address
implements Serializable
{
private String m_addr;
private String m_city;

public Address()
{
}

public String getAddr()
{
return m_addr;
}

public void setAddr(final String addr)
{
m_addr = addr;
}

public String getCity()
{
return m_city;
}

public void setCity(final String city)
{
m_city = city;
}
}

I can write it to the servlet output stream as follows:

private void writeAMF(
final HttpServletRequest httpServletRequest,
final HttpServletResponse httpServletResponse
) throws IOException
{
final Address address = new Address();
address.setAddr( httpServletRequest.getParameter("addr"));
address.setCity( httpServletRequest.getParameter("city"));
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final Amf3Output amf3Output = new Java15Amf3Output(new SerializationContext());
amf3Output.setOutputStream(baos);
amf3Output.writeObject(address);
amf3Output.writeObjectEnd();
amf3Output.flush();

httpServletResponse.setContentType(MessageIOConstants.AMF_CONTENT_TYPE);
httpServletResponse.setContentLength(baos.size());
baos.writeTo(httpServletResponse.getOutputStream());
}

On the client side, I used a URLStream instance with an AMF3 object encoding to HTTP GET the result as an ActionScript object as follows:

private function load() : void
{
var urlRequest : URLRequest = new URLRequest( "http://localhost:8080/webamf/rest/amf" );
var urlVariables : URLVariables = new URLVariables();
urlVariables.addr = addr.text;
urlVariables.city = city.text;
urlRequest.data = urlVariables;

var urlStream : URLStream = new URLStream();
urlStream.objectEncoding = ObjectEncoding.AMF3;
urlStream.addEventListener(Event.COMPLETE, completeHandler );
urlStream.load( urlRequest );
}
private function completeHandler( event : Event ) : void
{
var urlStream : URLStream = event.target as URLStream;
address = urlStream.readObject() as Address;
}

Where Address is tagged as a remote class aliased to the server side Address class:

package com.esri.webamf
{
[Bindable]
[RemoteClass(alias="com.esri.webamf.Address")]
public class Address
{
public var addr : String;
public var city : String;

public function Address()
{
}
}
}

Cool ?

Thursday, October 2, 2008

Graphic Custom Symbols

We refactored the Symbol class for the next release of the Flex api for ArcGIS Server so it can be easily customized. Here is a simple custom symbol:

package com.esri.symbol
{

import com.esri.ags.Map;
import com.esri.ags.geometry.Geometry;
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.symbol.Symbol;

import flash.display.Sprite;

/**
* Simple custom class with variable color property.
*/
public class CustomSymbol extends Symbol
{
private var m_color : Number;

/**
* Constructor
* @param color optional color. Default 0xFF0000
*/
public function CustomSymbol( color : Number = 0xFF0000 )
{
m_color= color;
}

[Bindable]
/**
* Bindable color property. dispatch Event.CHANGE on change.
* Graphic objects are CHANGE event listeners, and will invalidate their display list
* upon the receipt of the event.
*/
public function get color() : Number
{
return m_color;
}
/**
* @private
*/
public function set color( value : Number ) : void
{
if( value != m_color )
{
m_color = value;
dispatchEventChange();
}
}

/**
* Clear the sprite.
* Recommended that you do this here not in the draw function.
*/
override public function clear( sprite : Sprite ) : void
{
sprite.graphics.clear();
}

/**
* Draw the geometry.
* Here we only render MapPoints.
*/
override public function draw(
sprite : Sprite,
geometry : Geometry,
attributes : Object,
map : Map
) : void
{
if( geometry is MapPoint )
{
drawMapPoint( sprite, MapPoint(geometry), map);
}
}

/**
* Draw the map point.
* Here we position the sprite and use raw flash graphics primitive to draw.
*/
private function drawMapPoint(
sprite : Sprite,
mapPoint : MapPoint,
map : Map
) : void
{
sprite.x = toScreenX( map, mapPoint.x );
sprite.y = toScreenY( map, mapPoint.y );

sprite.graphics.beginFill( m_color, 0.5 );
sprite.graphics.drawCircle( 0, 0, 10 );
sprite.graphics.endFill();
}

}
}

And here is how it is used:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
xmlns:symbol="com.esri.symbol.*"
layout="vertical"
>
<esri:Map openHandCursorVisible="false">
<esri:ArcGISTiledMapServiceLayer
url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"/>
<esri:GraphicsLayer>
<esri:Graphic toolTip="Hello, World!">
<esri:geometry>
<esri:MapPoint x="45" y="45"/>
</esri:geometry>
<esri:symbol>
<symbol:CustomSymbol color="{colorPicker.selectedColor}"/>
</esri:symbol>
</esri:Graphic>
</esri:GraphicsLayer>
</esri:Map>
<mx:ColorPicker id="colorPicker"/>
</mx:Application>

You can see it in action here.
Here is another example where I'm using Degrafa's drawing capabilities to create a simple pie chart symbol, where the wedges are proportional to the graphic attribute values. And finally, here is another example, where I'm using embedded and downloaded bitmaps. You can download the source code from here.

Tuesday, September 23, 2008

Styling, Skinning Map Navigation Control

The following is a simple sample that shows how to skin, style and reposition the map navigation control. All is done using CSS:

Navigation
{
borderSkin : ClassReference("NavBorderSkin");
showScaleInDataTip: true;
paddingLeft: 6;
paddingRight: 2;
paddingTop: 2;
paddingBottom: 2;
top: 30;
left: NaN;
right: 30;
}

NavigationMinusButton
{
disabledSkin: Embed(source="assets/globe_icon.png");
downSkin: Embed(source="assets/globe_icon.png");
overSkin: Embed(source="assets/globe_icon.png");
upSkin: Embed(source="assets/globe_icon.png");
}

NavigationPlusButton
{
disabledSkin: Embed(source="assets/home_icon.png");
downSkin: Embed(source="assets/home_icon.png");
overSkin: Embed(source="assets/home_icon.png");
upSkin: Embed(source="assets/home_icon.png");
}

Note that I have to set the value of left to NaN to override the default value. In addition, I'm using Degrafa to skin the border. Check out the sample here. And you can view the source code here.

Friday, September 12, 2008

ArcGIS Tiles on Amazon S3

Once you have generated your tiles from a map services, you can move these tiles to Amazon S3. I used the S3Fox Firefox extension to move the files from my local system to S3. In addition, make sure to copy the JSON map service metadata to a file named "MapServer" (without quotes of course :-). You can access these tiles using the Flex API for ArcGIS server as follows:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
xmlns:s3="com.esri.s3.*"
layout="absolute"
>
<esri:Map>
<s3:S3ArcGISTiledMapServiceLayer
url="http://s3.amazonaws.com/your_map_service/MapServer"/>
</esri:Map>
</mx:Application>

And here is the implements of S3ArcGISTiledMapServiceLayer class:

package com.esri.s3
{
import com.esri.ags.layers.ArcGISTiledMapServiceLayer;

import flash.net.URLRequest;

public class S3ArcGISTiledMapServiceLayer extends ArcGISTiledMapServiceLayer
{
private var m_baseURL : String;

public function S3ArcGISTiledMapServiceLayer(url:String=null)
{
super(url);
}

override public function set url(value:String):void
{
super.url = value;
if( value )
{
var index : int = value.lastIndexOf( "/" );
m_baseURL = value.substr( 0, index );
}
else
{
m_baseURL = "";
}
}

override protected function getTileURL(
level:Number,
row:Number,
col:Number
):URLRequest
{
return new URLRequest( m_baseURL + "/l" + level + "r" + row + "c" + col + ".jpg" );
}
}
}

Note that I simplified the storage retrieval in the getTileURL function. ArcGIS places the tiles on the file system as a set of sub-directories of the form level/row/column.jpg. I decided (out of laziness :-) to keep it flat and have a set of files in the form lXrXcX.jpg.

Wednesday, September 10, 2008

Skinning InfoWindow using Degrafa

Here is an example of how to skin the border of an InfoWindow using degrafa. This can be done using CSS by defining a skinBorder to InfoWindow as follows:

InfoWindow {
padding-left : 5;
padding-right : 5;
padding-top : 5;
padding-bottom : 5;
tip-width : 3;
tip-length : 3;
border-skin : ClassReference("IWBorderSkin");
}

The IWBorderSkin is a sub-class of a degrafa GraphicBorderSkin. Like usual, you can download the source from here.

Generate AS3 VOs from Jar file

A lot of AS3 value object generators assume that you have the source code. However sometimes you are not privy to this information :-( So here is a java program that reads the content of a jar file and creates AS3 VOs. Like this post, I'm using StringTemplate to generate the files. And this other post was very helpful too. I'm using IntelliJ as my IDE and it lets me generate an executable jar. The following is the usage:

java -jar class2as3.jar jarPath packageName outputPath

You can download the source code from here.

Tuesday, September 9, 2008

Custom Symbol for Graphic using Flex API for ArcGIS

This is demonstration of a very simple custom symbol to render a graphic component on a map. Here is the application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
xmlns:test="com.esri.test.*"
layout="absolute"
>
<esri:Map>
<esri:GraphicsLayer>
<esri:graphicProvider>
<mx:ArrayCollection>
<esri:Graphic>
<esri:geometry>
<esri:MapPoint x="45" y="45"/>
</esri:geometry>
<esri:symbol>
<test:CustomSymbol/>
</esri:symbol>
</esri:Graphic>
</mx:ArrayCollection>
</esri:graphicProvider>
</esri:GraphicsLayer>
</esri:Map>
</mx:Application>

And here is the CustomSymbol:

package com.esri.test
{
import com.esri.ags.Graphic;
import com.esri.ags.Map;
import com.esri.ags.esri_internal;
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.symbol.Symbol;

use namespace esri_internal;

public class CustomSymbol extends Symbol
{
public function CustomSymbol()
{
super();
}

override esri_internal function drawGraphic(
map : Map,
graphic : Graphic
) : void
{
if( graphic.geometry is MapPoint )
{
drawMapPoint( map, graphic, MapPoint( graphic.geometry ));
}
}

private function drawMapPoint(
map : Map,
graphic : Graphic,
mapPoint : MapPoint
) : void
{
graphic.x = map.mapToContainerX(mapPoint.x);
graphic.y = map.mapToContainerY(mapPoint.y);

graphic.graphics.clear();
graphic.graphics.beginFill( 0xFF0000, 0.5 );
graphic.graphics.drawCircle( 0, 0, 10 );
graphic.graphics.endFill();
}

}
}

Be warned - this uses an esri_internal function that is subject to change without notice :-(

Tuesday, August 26, 2008

Heat Map + Papervision3D

Doug McCune (one of my all time Flex heroes) contributed heavily to SpatialKey, which looks and works great. Got to see it last week at 360|Flex in San Jose. I've been wanting to do something similar, but in 3D! Dabbled a bit with Papervision3D, but wanted to get into it, and that was the perfect opportunity. The idea that I had was to read a country shapefile, and thematically map all the cities in that country based on some attribute. And do all that on the client using Flex. I did a little bit of research, and here is a list of sites that "inspired" me:
And here is a snapshot of the result:


You can download the source code of the application from here. And you can see a sample demo here.

Sunday, August 10, 2008

Quantile Thematic Mapping

In a previous post, I showed how to bind the output of a query task to a graphic layer. The graphic layer had a default symbol that was used to render the graphic features. In the following example, I would like to symbolize each feature based on an attribute value. We could use the symbolFunction property of a graphic layer, however this function is called on each graphic feature and does not have a "grand picture" of the full set. This is the case of a quantile distribution, where the features are rank-ordered and equal number of features are placed in colored bucket. Here is the result:Each country is color coded based on a quantile distribution of its area property over a specified number of classes. Here is the code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
xmlns:sample="com.esri.ags.sample.*"
layout="absolute"
>
<sample:QuantileAreaCalculator
numClasses="{numClasses.value as Number}"
graphicProvider="{queryTask.executeLastResult.features}"/>
<esri:QueryTask id="queryTask" url="http://tejas:8399/arcgis/rest/services/maps/world/MapServer/1">
<esri:Query id="query" returnGeometry="true" where="{where.text}">
<esri:outFields>
<mx:String>*</mx:String>
</esri:outFields>
</esri:Query>
</esri:QueryTask>
<mx:Panel width="100%" height="100%" title="Quantile Thematic Mapping">
<mx:HBox width="100%" height="100%" horizontalGap="0">
<esri:Map id="map">
<esri:ArcGISDynamicMapServiceLayer
url="http://tejas:8399/arcgis/rest/services/maps/world/MapServer"/>
<esri:GraphicsLayer id="graphicsLayer"
graphicProvider="{queryTask.executeLastResult.features}">
</esri:GraphicsLayer>
</esri:Map>
<mx:DataGrid dataProvider="{queryTask.executeLastResult.attributes}" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="NAME"/>
<mx:DataGridColumn headerText="Area" dataField="AREA"/>
</mx:columns>
</mx:DataGrid>
</mx:HBox>
<mx:ControlBar>
<mx:TextInput id="where" text="FID < 10"/>
<mx:Button label="Query" click="queryTask.execute()"/>
<mx:ComboBox id="numClasses">
<mx:ArrayCollection>
<mx:Number>2</mx:Number>
<mx:Number>3</mx:Number>
<mx:Number>4</mx:Number>
<mx:Number>5</mx:Number>
</mx:ArrayCollection>
</mx:ComboBox>
</mx:ControlBar>
</mx:Panel>
</mx:Application>

I introduced the QuantileAreaCalculator Class that intercepts the query last result and performs a quantile distribution of the features. A graphic feature is assigned to a bucket, and is assigned the symbol associated with that bucket.
That last assignment forces the graphic to redraw itself. Thus the above result.
The QuantileAreaCalculator numClasses property is bound to the numClasses combo box value, where upon a value change, the distribution is recalculated and the layer is auto refreshed. Download the full source code from here.

Friday, August 8, 2008

3D Multinodal Routing Application

Was tasked to build a Flex based 3D multi-nodal routing application for ESRI's International User Conference. Check out it here. The application enables the user to select a starting and ending room, then an optimized route is calculated using an ArcGIS GeoProcess task. The returned features are rendered in 3D and symbolized based on the floor level and the transition type, such as escalator, stairs or elevator. The floor and room outlines are derived from embedded shapefiles. I used Papervision3D for the the 3D rendering. Though this was my first time dabbling with Papervision, the learning curve was easy and having access to the source code made it more approachable.

360|Flex

Just got approved to be there. Hope to connect with some of you guys !

Poor Man's Tracking

Short of using BlazeDS for asynchronous event notification, RSS is nice "standard" way to publish events. And since XML is a first class citizen in AS3, then parsing the feed is relatively easy. Yes, sure, I can use this syndication library, but it does not give me the control that I need over the feed items, specially over custom tags. One of these custom tags refer to GeoRSS, where you can add a geographical reference to the RSS entries. The geo tags can be of the form of points, lines or polygons. Here is a sample of a GeoRSS feed in JSP, where on every request a random lat/lon point value is generated. This can easily be extended to get the values from a database or a GPS source:

<%@ page contentType="text/xml" language="java" %>
<%
final double lat = -45.0 + 50.0 * Math.random();
final double lon = -45.0 + 50.0 * Math.random();
%>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:georss="http://www.georss.org/georss">
<title>Earthquakes</title>

<subtitle>International earthquake observation labs</subtitle>
<updated>2005-12-13T18:30:02Z</updated>
<author>
<name>Mansour Raad</name>
<email>mraad@esri.com</email>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<entry>
<title>M 3.2, Mona Passage</title>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2005-08-17T07:02:32Z</updated>
<summary>We just had a big one.</summary>
<georss:point><%=lat%> <%=lon%></georss:point>
</entry>
</feed>

To read and render the entries on a map, I've extended the GraphicLayer class to create a GeoRSSLayer and it is used as follows:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
xmlns:georss="com.esri.ags.georss.*"
>
<mx:TraceTarget/>
<esri:Map>
<esri:ArcGISTiledMapServiceLayer
url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"/>
<georss:GeoRSSLayer
url="http://localhost:8080/georss.jsp"
delay="15000">
<georss:symbol>
<esri:SimpleMarkerSymbol style="circle" color="0x00FFFF" size="20"/>
</georss:symbol>
</georss:GeoRSSLayer>
</esri:Map>
</mx:Application>

The GeoRSS class has two additional attributes. The url attributes points to the georss feed location, and the delay attribute indicates the delay time in milliseconds to check for new feed entries. You can download the full source code from here. This is assuming that you are using the Flex API for ArcGIS :-)

Saturday, August 2, 2008

Callout Marker Symbol

Point locations in the new Flex API for AGS are rendered using a Symbol subclass. One of these classes is the InfoSymbol, where an info window (callout) will be displayed anchored at the point location. Developers can specify the content of the symbol as a DisplayObject sublcass. The InfoSymbol was inspired from the FIG: Callout Component.
Below is a sample. Click on it to see it in action, and you can download the source code from here.

Flex API For ArcGIS Server in action

This is one of my favorite usage of the API in pure MXML. I'm taking advantage of data binding to represent the output of a Query Task in graphical and textural format. In this application, I've created a QueryTask to a specific layer (countries) in an ArcGIS service (world). The QueryTask has a Query child specifying that the geomerty of the features should be returned, in addition to all the attribute fields. The application UI consists of a side by side map and data grid, and below those is a text field to define a "where" clause, and a button to execute the query. The map has two layers, a dynamic layer to show the world and a graphic layer to show the selected countries. When a user clicks the Query button, the Query "where" attribute is populated (via binding) with the textfield content and the QueryTask is executed. Upon a successful result, the graphic layer will be populated with new graphic objects and the data grid will contain new rows.

The binding "magic" is in the following two lines:

graphicProvider="{queryTask.executeLastResult.features}"

dataProvider="{queryTask.executeLastResult.attributes}"

where the graphic layer will be populated with the query task last result features, and the data grid will be populated with the query task last result attributes. Pretty cool, eh ? And the best part...no ActionScript :-)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:esri="http://www.esri.com/2008/ags"
xmlns:sample="com.esri.ags.sample.*"
layout="absolute"
>
<esri:QueryTask id="queryTask" url="http://tejas:8399/arcgis/rest/services/maps/world/MapServer/1">
<esri:Query id="query" returnGeometry="true" where="{where.text}">
<esri:outFields>
<mx:String>*</mx:String>
</esri:outFields>
</esri:Query>
</esri:QueryTask>
<mx:Panel width="100%" height="100%">
<mx:HBox width="100%" height="100%" horizontalGap="0">
<esri:Map id="map">
<esri:ArcGISDynamicMapServiceLayer
url="http://tejas:8399/arcgis/rest/services/maps/world/MapServer"/>
<esri:GraphicsLayer id="graphicsLayer"
graphicProvider="{queryTask.executeLastResult.features}">
<esri:symbol>
<esri:SimpleFillSymbol color="0xFF0000"/>
</esri:symbol>
</esri:GraphicsLayer>
</esri:Map>
<mx:DataGrid width="100%" height="100%"
dataProvider="{queryTask.executeLastResult.attributes}">
<mx:columns>
<mx:DataGridColumn headerText="Name" dataField="NAME"/>
<mx:DataGridColumn headerText="FIPS" dataField="FIPS_CODE"/>
<mx:DataGridColumn headerText="Area" dataField="AREA"/>
</mx:columns>
</mx:DataGrid>
</mx:HBox>
<mx:ControlBar>
<mx:TextInput id="where" text="FID < 10"/>
<mx:Button label="Query" click="queryTask.execute()"/>
</mx:ControlBar>
</mx:Panel>
</mx:Application>

Flex API For ArcGIS Server

Ok gang, sorry for the long silence. We have been busy here @ ESRI with building the Flex API for ArcGIS Server. We will be announcing the public beta at the user conference this week and here is the link. Download it, try it out and give us some feedbacks.

Sunday, May 18, 2008

ArcGIS Flex API in use in China

Check out this entry - pretty cool implementation of the AGS Flex API - our prayers and thoughts go out to the folks that perished in this terrible incident - Peace.

Blogged with the Flock Browser

Thursday, May 15, 2008

Thursday, May 1, 2008

SpringSource Application Platform

The SpringSource folks announced the beta release of an Application Platform. This is very cool, where in the past you had to have a web server to start up Spring. And if you are a big fan of OSGi and Spring-DM (like I am) you know what a pain it was to wedge all that together. And if you think about it, it was a chicken and egg problem. Well no more, this platform does it "right", where it starts the JVM, the OSGi platform (Equinox), the new Dynamic Module Kernel™ (dm-Kernel) and then the bundles. Remember back when SpringSource bought or aligned (do not remember which) with the Tomcat folks, well Tomcat is now a web bundle. This is very cool - next is to move it onto EC2.

Monday, April 21, 2008

FlexUnit + ANT + AIR

I'm a big fan of Unit Testing and Continuous Integration. Peter Martin blogged about how to achieve this for Flex. Basically, he created an Ant task that starts a server socket connection and forks a browser that launches a flexunit based application. The application opens a client socket connection back to the server and streams the flexunit results in JUnit XML format. The server reads the xml stream and writes it on the local disk.
Notice that all of this is done so that a flex application in a browser can write to the local disk. Pretty clever, but this is where AIR makes life a bit easier. A Flex application using AIR can write to a local disk drive. Since the source code of JUnitTestRunner was available (thanks Peter), I was able to 1) learn something and 2) adjust the sendResults function to write the result to disk.

private function sendResults() : void
{
for each (var report : Object in reports)
{
var xml : XML = createXMLReport(report);
var utf : String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml.toString();

var file : File = new File();
if( m_basedir )
{
file = file.resolvePath( m_basedir + "/target/TEST-" + xml.@name + ".xml");
}
else
{
file = File.userDirectory.resolvePath("TEST-" + xml.@name + ".xml");
}
var fs : FileStream = new FileStream();
fs.open(file, FileMode.WRITE);
fs.writeUTFBytes(utf);
fs.close();
}
}

Next, was to compile the application in Ant as part of the CI process. Here is the target that calls mxmlc:

<target name="compile">
<java jar="${flex.home}/lib/mxmlc.jar" fork="true" failonerror="true" maxmemory="256m">
<jvmarg value="-Dapplication.home=${flex.home}"/>
<jvmarg value="-Dsun.io.useCanonCaches=false"/>
<arg line="-compiler.headless-server"/>
<arg line="-library-path+='libs/flexunit.swc'"/>
<arg line="-output='target/junitair.swf'"/>
<arg line="+configname=air"/>
<arg line="src/junitair.mxml"/>
</java>
<copy file="src/junitair-app.xml" todir="target"/>
</target>

The last task in the target copies the application configuration to the build directory. I have modified the configuration file with the following entry:

<content>junitair.swf</content>

Now to run the application - I admit that I cheated here a bit :-( where, rather than creating a "real" AIR application with it full signature and having to "allow" it to run everytime as it has been changed during the compilation, I use the adl executable as follows:

<target name="run">
<exec executable="adl" failonerror="true">
<arg value="target/junitair-app.xml"/>
<arg value="--"/>
<arg value="${basedir}"/>
</exec>
</target>

I've created a project that contains a build.xml and the junitair.mxml application. Make sure to download the flex sdk. Create an environment variable FLEX_HOME that points to where you installed the sdk, and make sure that $FLEX_HOME/bin is in your path so that adl can be execute.

Thursday, April 17, 2008

Glassfish on OSGi

Not to say that I predicted this at the last JavaONE - but JBoss is going to be based on OSGi and now so is Glassfish. Too cool - you will see more and more web servers will be based on OSGi and with Spring-DM to wire your POJOs - That is how you write enterprise software.
Blogged with the Flock Browser

Sunday, April 13, 2008

M$ Joke

A helicopter was flying around above Seattle when an electrical malfunction disabled all of the aircraft's electronic navigation and communications equipment.

Due to the clouds and haze, the pilot could not determine the helicopter's position. The pilot saw a tall building, flew toward it, circled, and held up a handwritten sign that said "WHERE AM I?" in large letters. People in the tall building quickly responded to the aircraft, drew a large sign, and held it in a building window. Their sign said "YOU ARE IN A HELICOPTER."

The pilot smiled, waved, looked at his map, determined the course to steer to SEATAC airport, and landed safely. After they were on the ground, the copilot asked the pilot how he had done it.

"I knew it had to be the Microsoft Building, because they gave me a technically correct but completely useless answer."
Blogged with the Flock Browser

Saturday, April 12, 2008

Monday, March 24, 2008

ArcGIS Server Flex API

Just got back from ESRI's Developer Summit in Palm Spring, where I presented the ArcGIS Server Flex API. You can download my presentation from here. This API is intended to be released post 9.3 and will be based on the REST protocol and is very closely modeled after the JavaScript API. In the presentation, I showcased how to build a map application and how to create dynamic and tiled layers. In addition, I added graphic layers with adjustable graphics and demonstrated how to subclass a GraphicLayer to create a GeoRSS layer.

Saturday, March 8, 2008

Unable to resolve '[file]' for transcoding

So I was happy building my project in Flex Builder, till I committed my code and CruiseControl complained that I cannot build it ! See, CC uses compc (the command line compiler) to compile the module and it was returning:

unable to resolve 'assets/cross_cursor.png' for transcoding.

Here is what the code looked liked.

[Embed(source="assets/cross_cursor.png")]

So I said to my self, "Oh come on! I thought you can handle pngs. Poop !" - Googled the error and found this link. And the solution was to add a "/" in front of "assets":

[Embed(source="/assets/cross_cursor.png")]

Now I'm happier :-)

Wednesday, February 20, 2008

Placing a label on the map

This examples shows how to place a label on the map. Actually, you can place any UIComponent. But make sure to set the width and height of the component :-)

<?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"
xmlns:test="com.esri.test.*"
layout="absolute"
>
<test:LabelStyle id="labelStyle"/>
<awx:Framework apiKey="19640523"/>
<awx:Map createDefaultLayers="false">
<awx:MarkerLayer>
<test:LabelMarker geoX="45" geoY="45" style="{labelStyle}"/>
</awx:MarkerLayer>
</awx:Map>
</mx:Application>

The LabelStyle places the overlay on the map in pixel coordinates.

package com.esri.test
{
import com.esri.aws.awx.geom.PathType;
import com.esri.aws.awx.map.layers.overlays.OverlayObject;
import com.esri.aws.awx.map.layers.overlays.style.BaseStyle;
import com.esri.aws.awx.map.projection.IProjectionModel;

public class LabelStyle extends BaseStyle
{
override public function draw(
overlayObject:OverlayObject,
projectionModel:IProjectionModel
):void
{
var pathType:int = overlayObject.shape.getPathIterator(projectionModel).next(m_coords);
if (pathType == PathType.NOOP)
{
overlayObject.visible = false;
}
else
{
overlayObject.visible = true;
overlayObject.x = m_coords[0];
overlayObject.y = m_coords[1];
}
}
}
}

And here is the MarkerLabel class:

package com.esri.test
{
import com.esri.aws.awx.map.layers.overlays.Marker;

import mx.controls.Label;

public class LabelMarker extends Marker
{
override protected function createChildren():void
{
super.createChildren();
var label : Label = new Label;
label.text = "Hello, World!";
label.width = 200;
label.height = 24;
addChild( label );
}
}
}

BTW - There has to be a way to calculate the width and height of a string using the Font class (not sure if this class even exists) or something. I guess that is another blog entry :-)

Sunday, February 10, 2008

Sending EMail From Flex App

Check out this mailto: RFC.
var urlRequest : URLRequest = new URLRequest( "mailto:?subject=MySubject&body=MyBody");
navigateToURL( urlRequest, "_self" );
Make sure to add the _self target to not open a blank browser window.

Monday, February 4, 2008

Calling Find service from AJAX

Here is a sample that shows how to call the Find service from AWXBridge.  Try 'Disneyland' (no quotes :-)

<html>
<head>
<title>Find Sample</title>
<style>
body {
margin: 0px;
}
</style>
</head>
<body>
<div id="awx" style="width:100%; height:500px;">
<a href="http://www.adobe.com/go/getflashplayer">Download latest Adobe Flash Player</a>
</div>
<input id="placeInput" type="text">
<input type="button" value="Find" onclick="doFind()">
<script type="text/javascript" src="http://api.arcwebservices.com/awx/v4/awxbridge-4.0.js"></script>
<script language="JavaScript" type="text/javascript">

var m_root;
var m_map;

function onCreationComplete()
{
try
{
m_root = FABridge.awx.root();

m_root.installFindActivator();

m_map = m_root.createMap();

m_root.addChild(m_map);
}
catch(ex)
{
alert(ex.message);
}
}

function onResult(result)
{
try
{
var arr = result.getFindResults();
for (var key in arr)
{
var findResult = arr[key];
var point = findResult.getPoint()
m_map.setCenter(point);
m_map.setScale(1000000);
break;
}
}
catch(ex)
{
alert(ex.message);
}
}
function onFault(fault)
{
try
{
alert(fault.getMessage());
}
catch(ex)
{
alert(ex.message);
}
}
function findPlace(place)
{
try
{
var responder = m_root.createResponder(onResult, onFault);
var find = m_root.getFind();
find.findLocation(place, null, responder);
}
catch(ex)
{
alert(ex.message);
}
}
function doFind()
{
findPlace( document.getElementById("placeInput").value);
}

AWXBridge.insertFramework("awx", {apiKey:"7cf5e2b1c266030dad57a03e41474775"});

</script>
</body>
</html>

Screen cleaner

Check this out - way too funny for the dog lovers out there :-)

AWX and AJAX

I've seen a lot of interest lately in using AWX within an AJAX application. The preferred way is to use the AWXBridge API. This is based on Adobe's FABridge technology, and relies on a set of patterns to "bridge" the JavaScript and ActionScript world. Check out this example to see it in action. Here is a quick primer:
A JavaScript root object is created to refer to an embedded Flex application - the FABridge property, in this case 'example', is a reference to the DIV id where the Flex application SWF will reside.
var flexApp = FABridge.example.root();
Any ActionScript object with an empty constructor can be created from JavaScript using its full name:
var chart = FABridge.example.create("mx.charts.ColumnChart");
An AS object public property can be retrieved from JS using a getter function, where the name of the function is based on the property name prefixed with "get" and the first letter of the property is uppercased:
flexApp.getSlider().getValue();
An AS object public property can be modified from JS using a setter function, where the name of the function is based on the property name prefixed with "set" and the first letter of the property is uppercased:
var currentCheckValue = flexApp.getCheck().getSelected();
flexApp.getCheck().setSelected( ! currentCheckValue );
An AS object public function can be accessed directly.
var flexApp = FABridge.example.root();
flexApp.testFunc( "Hello, Actionscript World! Love, Javascript..." );
So, armed with a that tidbit of knowledge, to bridge AJAX and AWX, please refer to the ASDoc to get a list objects, properties and functions that you can call. A bit more of an insight, the awxbridge.swf file that is downloaded into the div element contains a set of public helper functions to create a set of AWX classes. You can find the list of functions here and a how some of them are used here.

Sunday, February 3, 2008

Tracking the mouse on the map

This is done by setting the mouseHandler property on a map instance to an implementation of the IMouseHandler interface. The IMouseHandler interface has the classic mouseUp, mouseDown, mouseMove abstract functions. In addition, there are two abstract functions; set map() and cleanup(). The cleanup method is called on any previously set handler before a new mouse handler is set. A map reference is "injected" (a al IoC) onto the mouse handler when it is assigned to the map using the setter function. AWX provided a base implementation (BaseMouseHandler) that has a protected helper function to convert a stage mouse location to a GeoPoint. Another provided implementation, BaseOverlayMouseHandler associates a mouse handler with a map layer. This class can be used to draw overlays on map using the mouse such as redlines and rubberband rectangles. The following implementation enables a user to draw a geodesic circle on the map. You can see it in action here. Select the "Draw Circle" from the context menu to draw a circle. Download the full source code from here.

package com.esri.sample
{
import com.esri.aws.awx.geom.CircleShape;
import com.esri.aws.awx.geom.GeoPoint;
import com.esri.aws.awx.map.handlers.BaseOverlayMouseHandler;
import com.esri.aws.awx.map.layers.OverlayLayer;
import com.esri.aws.awx.map.layers.overlays.Circle;
import com.esri.aws.awx.map.layers.overlays.style.IStyle;
import com.esri.aws.awx.map.layers.overlays.style.PolygonStyle;
import com.esri.aws.awx.map.projection.ProjUtils;

import flash.events.Event;
import flash.events.MouseEvent;

[Event(name="complete", type="flash.events.Event")]

public class CircleMouseHandler extends BaseOverlayMouseHandler
{
private var m_down : Boolean = false;
private var m_center : GeoPoint;
private var m_shape : CircleShape;
private var m_circle : Circle;

public function CircleMouseHandler(
overlayLayer : OverlayLayer
)
{
super(
overlayLayer,
new PolygonStyle,
false
);
}

override public function onMouseDown(event:MouseEvent):void
{
m_down = true;
m_center = stage2GeoPoint( event );
if( m_center )
{
m_shape = new CircleShape( m_center, 1, CircleShape.METERS);
m_circle = new Circle( m_shape, style );
overlayLayer.addOverlay( m_circle );
event.updateAfterEvent();
}
else
{
m_down = false;
m_circle = null;
}
}

override public function onMouseMove(event:MouseEvent):void
{
if(m_down)
{
var geoPoint : GeoPoint = stage2GeoPoint(event);
if( geoPoint )
{
m_shape.radius = geodeticInverse(m_center, geoPoint, ProjUtils.EARTH_RADIUS );
m_circle.invalidateDisplayList();
event.updateAfterEvent();
}
}
}

override public function onMouseUp(event:MouseEvent):void
{
m_down = false;
if( m_circle )
{
if( applyToMap == false)
{
overlayLayer.removeOverlay( m_circle);
}
event.updateAfterEvent();
dispatchEvent( new Event(Event.COMPLETE));
}
}

private function geodeticInverse(
fPt:GeoPoint,
tPt:GeoPoint,
earthRadius:Number
) : Number
{
var lam1:Number = ProjUtils.DDToRad(fPt.x), phi1:Number=ProjUtils.DDToRad(fPt.y);
var lam2:Number = ProjUtils.DDToRad(tPt.x), phi2:Number=ProjUtils.DDToRad(tPt.y);
var d_lam:Number = ProjUtils.Angle180(lam2 - lam1);

var cos_phi1:Number = ProjUtils.AngleIsHalfPi(Math.abs(phi1)) ? 0.0 : Math.cos(phi1);
var sin_phi1:Number = Math.sin(phi1);
var cos_phi2:Number = ProjUtils.AngleIsHalfPi(Math.abs(phi2)) ? 0.0 : Math.cos(phi2);
var sin_phi2:Number = Math.sin(phi2);

var cos_dlam:Number = ProjUtils.AngleIsHalfPi(Math.abs(d_lam)) ? 0.0 : Math.cos(d_lam);
var sin_dlam:Number = ProjUtils.AngleIsZero(Math.abs(d_lam)-Math.PI) ? 0.0 : Math.sin(d_lam);

var sigma:Number = Math.acos(sin_phi1*sin_phi2 + cos_phi1*cos_phi2*cos_dlam);
var distance:Number = sigma * earthRadius;
return distance;
}
}
}

Saturday, February 2, 2008

Reading Local Shapefiles

ESRI shapefiles are fairly ubiquitous. That is why I keep getting requests like this “Hey, I would like to render my local shapefiles on top the AWX map, can I do that ?” And I reply “Sure...but !” - Remember the security sandbox of the flash player; 1 - you can only communicate with the server that hosts the swf, or with a host that has a crossdomain.xml file, and 2 - there is no way that you can access your local disk drive directly - So, let’s turn these two restrictions into assets :-) I’m luck enough to run on a Mac, which comes with Apache Web Server built in. So, if I place a crossdomain.xml file at the base url of my local server, then I can access my local shapefiles. Cool eh ? Next, found this AS3 library that can read the shp and dbf file given a ByteArray instance. Here is a code snippet that I used on a project to overlay the continents shapes on top of the map.

private function onCreationComplete( event : FlexEvent ) : void
{
var urlStream : URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE,
function( event : Event ) : void
{
var byteArray : ByteArray = new ByteArray();
urlStream.readBytes( byteArray, 0.0, urlStream.bytesAvailable);
urlStream.close();

var style : IStyle = new PolygonStyle( 0xCCCCCC, 0.25, 1, 0x0 );

var shpHeader : ShpHeader = new ShpHeader( byteArray);
while( byteArray.position < byteArray.length)
{
var shpRecord : ShpRecord = new ShpRecord( byteArray );
var shpPolygon : ShpPolygon = shpRecord.shape as ShpPolygon;
for each ( var arr : Array in shpPolygon.rings)
{
var coords : Array = [];
for each ( var sp : ShpPoint in arr )
{
coords.push( new GeoPoint( sp.x, sp.y));
}
var polygon : Polygon = new Polygon( new PolygonShape( coords), style);
polygonLayer.addOverlay( polygon );
}
}
}
, false, 0.0, true);
urlStream.load( new URLRequest("shapefiles/continents3.shp"));
}