Sunday, December 20, 2009

Street View Within Flex API for AGS

There is this great sample in our code gallery where a Google Streetview is integrated with a map based on the Flex API for AGS. However, what I wanted is to have the street view to be part of the map, not an HTML sibling element. Found this project (flex-iframe) on google code, where you can display an HTML page inside a flex application. So I went ahead and merged the two projects. One of the requirements is to modify the template html file that is generated by Flex Builder with additional javascript code that enables the bridging between the flash and javascript world. I was not thrilled with fooling around with generated code, then I remembered the JavaScript flex tag by Abdul Qabiz. This flex element enables you to put javascript code _in_ a flex application, and on initialization it will be "evaled" and injected into the parent document DOM. So I merged all three projects together and this is you get. Check out it and tell me what you think. And like usual, the source code is here. There are some gems in there, like a custom symbol and I've adjusted the JavaScript flex tag. I've tried the app on my mac using safari, firefox and chrome, and it worked fine. However, I had issues with firefox on vista. IE worked for me.

Sunday, November 22, 2009

Yet Another Resizable TitleWindow

Was kinda ticked that TitleWindow did not have a resizable property :-( Found this reference when I googled "ResizableTitleWindow" - but was way too heavy. I just needed a _simple_ resizable title window, where I can grab the lower right corner and drag it to resize the window. Then I remembered that flexlib has a MDI package and I was always impressed with the code quality :-) Found the most excellent code for the MDIWindow component with resizable behaviour and additional features that I did not need. Here is my yet simplified derived version. And like usual, you can down the source code from here. BTW, I leave it as an "exercise" to the reader to add a visual element to the title window to indicate that it is resizable.

Saturday, November 14, 2009

Spatial / Temporal MBTA HeatMap

MassDOT issued a visualization challenge, where it is calling on developers to use released CharlieCard data to visualize "A Day in the Life of the MBTA". The following three applications use the Flex API for ArcGIS as a response to this challenge. The first application (click here to view it) consists of two maps. The first map contains a dynamically generated heat layer that morphs based on the selected hour. The second map is an isometric projection, but the T lines are represented in a schematic layout rather than a geographical layout. A schematic layout is what you see when you step up to a metro map at the station. You, the user, can rotate the schematic map by holding down the left mouse and dragging it left or right (no zoom in, sorry). At the bottom of the application is a horizontal slider. As you drag the slider, you are changing the "current" hour and minutes of the day. On the geographic map, the heat layer will reflect the CharlieCard counts per station. As to the isometric map, bars will extrude from every station, where the height of the bar is proportional to the CharlieCard count. There is a PLAY button, to play the hours of the day for you to step back and watch. The second application (click here to view it) is a simplified version of the first, where I embedded the clock in the map and removed the isometric map. This was nice and uncluttered, perfect for a dashboard on a large screen, say at the MBTA headquarters (hint, hint :-). The third application (click here to view it), is the same as the second one, but I added a table that lists all the active stations in descending count order.


Now, let me tell you how I built these applications. As I mentioned earlier, These are all based on the Flex API for ArcGIS. The base map image tiles are retrieved from ArcGIS Online. The geographical and isometric lines and stops are read from embedded shapefiles. The hour statistics are post-processed using ye-good-olde Excel from the MBTA released data. The result is a tab separated fields file, where each line in the file has the station name, the hour, and the sum of CharlieCards for that hour at that station. Again, that file is embedded in the application. Might take a bit of time to load the application, but all the spatial and temporal information is now on the client for local manipulation, after all this _is_ an Rich Internet Application.


What enabled me to quickly create three views of the same application is what I would like to talk to you next and is the guiding principles that I use to develop most of my "simple" applications. Three letters: M, V, C. You are now saying to yourself "yes, yes, Mansour...so what framework did you use ?" Actually....none! I can hear now. "Oh no, the (nasty word)... created _yet_ another framework". Actually I did not. I do have to admit that I did look at the usual suspects (Cairngorm, PureMVC, Swiz, Mate) but really did not need the extra swcs and the weight. What I _do_ need, is the MVC philosophy. But, can I do it with just the Flash and Flex provided classes? Yes, so here is the micro architecture and thought process. The Model is a singleton (O' oh, Singletonitis... hold on, hear me out) that holds the application state. It contains [Bindable] properties. Views (subclasses of UIComponent) are bound to the Model using "{}" in MXML (one of the beauties of Flex) or through ChangeWatcher instance in AS3. If a view or a non-view element wants to change the Model, it dispatches an event encapsulating all the values to change the Model. A controller instance will be listening for that event type and in its event handler code will process the encapsulated values and modifies the Model. As the view is bound to the Model, then the view will reflect any changes in the matter that it desires such as a set of map graphics or rows in a data grid. And the process (create an event, dispatch the event, controller listens for the event, processes the event and modifies the Model, view is bound to the Model and changes) repeats itself, over and over. You _will_ be temped to modify the Model directly from the view, but that will be an instant gratification solution. As the application development progresses, you _will_ find yourself needing to encapsulate that Model modification, thus the introduction of a controller. So, my advise is do it right from the beginning. Now, to enable that central event dispatching, we take advantage of the Application being an instance of EventDispatcher. To not confuse the Flex events with my events, all my event types are suffix with "$" such as "complete$". Last but not least, is the instantiation of the controllers and making them event listeners. As a matter of convention, all controllers names are prefixed with the event type, such as "CompleteController", and are declared as immediate children tag to the mx:Application tag in the MXML. For example:

<mx:Application>
<controller:CompleteController/>
<controller:LoadDataController/>
...
<esri:Map/>
<view:MyView/>
</mx:Application>

What does this mean? This tells the runtime engine to create anonymous instances of these controller classes. Each controller should have an empty constructor that registers it as a listener to the event to process and modify the Model.

public class CompleteController
{
public function CompleteController
{
Application.application.addEventListener( "complete$", handler );
}
private function handler( event : Event ) : void
{
// Process, process....modify Model.
}
}

With this process in mind, you now are structured to take every application feature and break it down into these three pieces which makes you focus on the task at hand. In addition, with unit testing (another blog topic) feature closure will emerge enabling you to move on to the next task. Yes, you do become what I call a "code monkey", but this is where the usage of for example TextExpander on my mac can automagically generate code based on a template. Side Note - One of these days, gotta write an eclipse plug-in :-)
Finally, have to give credit where credit is due, as I would have not been able to put this application together so quickly without "borrowing" code from:

Edwin van Rijkom for the shapefile library.

Juan Sanchez for the clock.

Michael VanDaniker for the heat map.

Keith Peters for the isometric code.

Nahuel Foronda for the Brownie skins

If I missed any person - it was not intentional. And like usual, you can download the source code from here. Hope you enjoyed this and tell me what you think.

Wednesday, September 23, 2009

Open Letter to Delta Airlines

Dear Delta,

First, have to prefix this letter with the following; being a million miler, I love flying with you and the little perks I get; like my wife being upgraded to first class on a 14 hour flight, while I hanged in the back with the kids - MAJOR POINTS for me _and_ you :-)
However (and here is the rub), I'm ticked at your ticketing system (kinda funny this play on word :-) about how it interacts, or most likely does not interact with our corporate travel department. See, I have an upcoming opportunity to travel coast to coast one week after another. When I submitted my two travel requests to fly Delta, Corporate kicked it back saying that Delta is more expensive ($200 and $300 more) than Virgin on one and AA on the other. Grrr, you don't fight Travel in these economic times. And...well I kinda wanted to try Virgin as I heard so much about it, but AA, no way! Had to admit defeat and take what was given to me, and then I started thinking. I do travel on one of these submitted trips very often and Delta _must_ know this. They _should_ have "bent" a little on the price for my business. I've been reading "Free: The Future of a Radical Price" by Chris Anderson (BTW, you can get his book for free) and I'm not saying I have to fly for free, but hear me out. What if you offered a free system that allows corporate travel departments to enter my frequent flier number, my upcoming trips, and (here is the important part) a form of a bid for these trips. In my case, $200 and $300 dollar less that the "list" price.
You, given my history and preferences, can look at me "holistically" and can accept that bid. Propose something that is close, say within $40, the "bend" which Travel will accept the difference, after all they _are_ trying to accommodate me. Or you can totally reject the bid. What I mean by holistically, is that in today's world of data mining from me (you have over 10 years worth of travel info on me), my fellow travelers and other external information, you _should_ be able to determine a price point (even at a _small_ loss today) that is acceptable to both parties, as you _will_ make it up and be profitable later. I'm sure somewhere in the bowels of your IT department you are brewing something like that. If not, then you should look at these recommendation engines (like what Amazon and Netflix has) for your frequent fliers. Anyway, this advise is free on the hope that such a holistic system will exist. Looking forward to traveling with you when the price is right to Corporate.

Regards,
Mansour

Friday, September 18, 2009

Map Layer From Local Shapefile

In this post, I will demo how to load a local shapefile from your hard drive, and overlay it on an map whose base layer is derived from a remote ArcGIS server. Using the Flash Player 10 FileReference API, this task is fairly easy. However, I did not want the user to be prompted for each imported file as a shapefile is composed of a .shp file holding the geometry and a .dbf file holding the attributes. So, the idea is to zip the shp and dbf and load the zip, and then let the application unzip the content and parse the shp and dbf entries. The application is using the Flex API for AGS and makes use of two wonderful as3 library; the shp library from Edwin van Rijkom, and a zip library from David Chang. In this application, I created a custom geometry to take advantage of the coordinate array format in the shapefile reducing the impedance and taking advantage of the "compression". Well, this not truly compressed - this an array of x,y,x,y,etc... rather than an array of MapPoint instances :-) and since this a custom geometry, well then you need a custom symbol to render that geometry. In this demo, I wanted to load and display the output of a plume model/process, and here is the result:

You can download the zip file from here, and you can see the application in action here. And like usual, the source code is here.
10/10/09 - I've updated the source to be a bit more robust - BTW, this only handles polygon shapes.

Monday, September 7, 2009

Horizontal Map Slider

A customer emailed me asking me how to create a horizontal map slider using the Flex API for AGS. The Map component has a property navigationClass to enable users to define a custom navigation class.

<esri:Map navigationClass="com.esri.sample.MyNavigation">

In this sample, I defined a reference to the class com.esri.sample.MyNavigation which is a subclass of the Navigation class.

public class MyNavigation extends Navigation
{
public function MyNavigation()
{
mx_internal::layoutObject.direction = BoxDirection.HORIZONTAL;
navigationSliderClass = MyNavigationSlider;
}

/**
* Override the order of the components.
*/
override protected function addZoomInZoomOutComponents(zoomInButton:UIComponent, zoomOutButton:UIComponent):void
{
addChild(new Spacer());
addChild(zoomOutButton);
addChild(zoomInButton);
addChild(new Spacer());
}
}

In the constructor, I defined the box direction (note the mx_internal :-) and my own custom slider. In addition, I've overwritten the addZoomInZoomOutComponents function to specify the order of the in/out buttons. Lastly, I defined a custom navigation slider to ensure the direction is horizontal.

public class MyNavigationSlider extends NavigationSlider
{
public function MyNavigationSlider()
{
direction = SliderDirection.HORIZONTAL;
maxHeight = 25;
}
}

Here is the final result, and like usual the source code is here.

Sunday, August 9, 2009

iPhone Isometric Shapefile Viewer

Not sure if some of you have see the 3D in-door routing application that I did for the ESRI 2009 User Conference. I used Papervision3D for this app, and was a lot of fun to develop. A couple of weeks before the conference, I decided to implement the same app on an iPhone using Obj-C. Being a newbie in the latter, I ramped up quickly on the language (got quickly over my hang ups on bracket, and kinda like the language :-). I started to look at the 3D built-in engine that uses a derivative of OpenGL and thought that I will not have the time to get this up and running. So I started to look for simpler alternatives. I read Keith Peters' most excellent AdvancED ActionScript 3.0 Animation book and used his A* implementation for client side routing application with barriers. check it out here. The next chapter in the book is all about isometric projection. This is exactly what I needed, an isometric view of the CDCC floors and rooms with a small twist, where the user can rotate the view.
So before doing a full Obj-C dive, I prototyped my application in Flex/AS3. The idea was to read a 3D shapefile (thanks Edwin), apply a rotation matrix on the original coordinates and then pass them through Peters isometric utility class. Once debugged and got it up and running (check out the app here, hold and drag the mouse to rotate the view), I ported the code to Obj-C. Check out this post to see some iPhone app snapshots. Like usual, here is the source code of the flex app.

Friday, July 31, 2009

Cool Flex API for AGS Samples

My friend and colleague _extraordinaire_ Kerry Coffin been working with the Flex API for AGS, and send me the following list of samples:

Linear Grid: This app shows a linear grid distance (miles, km etc) on a geographic map. Zoom In; pan north and south;change the number of grids;etc. Notice the grid width displayed. Also change the units.

Graticule: This app shows a Lat/Lon graticule in a web Mercator map. Zoom in continually. Notice how the graticule adjusts.

SimpleGraticule: This app shows a Lat/Lon graticule in a Geographic map. Zoom in continually. Notice how the graticule adjusts.

Quad Tree: This app shows a quad tree component that can index and search points in a client app. Add a bunch of points (Pick add 10K points). Drag the mouse to search. Try adding 10K more. Search again. The app shows you how the indexing is done.

Polynomial transformation: This app shows how to warp any image and overlay it on a map. (polynomial transformation) Press show resulting image. Change the order to 2. The user selects common points from both left and right. I’ve preloaded the app with a bunch of common points.

Normal Contours: Click to add elevation points. Pick a few points with elevation 100. Change the elevation to 105 pick a few more. Etc. Turn off the triangles to see only contours. (similar to the 3d app…except just contours)

And last my 3D app. It’s similar to using the contouring app.

MGRS: For the MGRS app, pan around the map by dragging your mouse. Of particular interest is the north pole, as most people leave off these zones, and they don’t draw curves.
If you zoom out, the scale dependency of the MGRS Layer turns off the finer grids. (MGRSLayer is implemented by extending Layer and implementing updateLayer()). The curves are drawn by doing the following algorithm: I don’t brute force it and add a trillion points and then project. To draw a line I project the 2 endpoints to pixel locations, and then project the mid point to its pixel location. I then compare the mid point’s projected pixel location to its pixel location by averaging the two endpoints. If the locations differ by more then one pixel…I do this to both halfs. This algorithm is done “logically” recursively…but I set a limit as to how many midpoints I add.

Monday, July 13, 2009

Indoor Routing for UC2009 on iPhone

Yes - just finished a native iPhone application (not using Safari) that uses the ArcGIS Server REST endpoint to create a 3D path between rooms in the San Diego Convention Center for the 2009 User Conference - this is _not_ an officially endorsed application (Yet :-) but for me, it was a great experience to see if I can get the iPhone to connect to AGS and render the result path in 3D as vector graphics. Yes, vector graphics - this is _not_ an image - if you drag your finger across the screen, the SDCC floor will rotate to give you a different perspective of the path. Cool eh ? Will be showing up at the UC on Monday, so stop by the Flex booth if you wanna see it live and have an AR version on the browser - AR you ask ? Just stop by :-) Here are some screenshots of the app - looking forward to seeing a lot of u there.






Thursday, May 28, 2009

Mapping EXIF Images With GPS Info

Was tasked to load an EXIF image with GPS info onto a map using the Flex API for AGS. So I first looked around for existing AS3 libraries and found ExifInfo. Downloaded it and tried it, but somehow it failed loading some EXIF images. Then I remembered a JavaScript library that a colleague pointed it out to me. So I ported the code to AS3 (optimization will come later) and here is the result. I used picasa to load some images and geo-tagged them using Google Earth (that is from the Tools menu BTW) and it worked pretty well. I borrowed some ideas and code from the author of ExifInfo and made my own EXIF mxml enabled class. Like usual here is the source code. BTW - There is a TextInput field at the bottom of the application, enter the url of your image (make sure that you have a crossdomain.xml at the base of your url) and if the image has EXIF GPS info, the map will display it. Try "assets/sample1.jpg" and hit enter.

Friday, May 22, 2009

[ANN] Flex Mapping API For AGS 1.2 is Released

We are proud to release the 1.2 version of the flex API for AGS - check it out here. New stuff include the support for Virtual Earth map tiles and geocoding. There is also now support for Network Analyst routing/sequencing with time windows and lots of bug fixes and enhancements. Hope you all like it.

Monday, May 18, 2009

My 360|Flex Presentation

As promised - just finished my presentation and here it is - have fun. Oh, here is the abstract: In this session we will demonstrate the Flex Mapping API For ArcGIS Server, where we will deconstruct live real-world mapping applications and algorithms such as dense clustering, auto labeling of features, collaborative editing, real-time asset tracking and client/server collaborative Geo-Processing. In addition, we will demonstrate how to create your own layer, geometry and symbol extensions for superior rendering performance taking advantage of the FP10 drawing API.

Monday, May 4, 2009

Map Points Cubic Clustering

Here is yet another clustering application. This is very similar to GeoCubes. It is based on a simple but effective algorithm, where the map extent is divided into "cubes". Given a map point from a set, the associated cube that it lies above is selected and the cube internal count is incremented. After processing all map points, the set of cubes with an internal count greater that one are displayed. Here is the application in action. And like usual, the source code is here.

Street Path Finding in AS3

Just finished reading Keith Peter's ActionScript 3.0 Animation book, and I very highly recommend it. Chapter 4 is all about pathfinding using A* but is based on a tiled grid network. So I adjusted it, in such a way that the nodes and arcs are derived from street features off a shapefile. The resulting application enables you to select a starting and ending street intersection and a path is displayed. In addition, you can add street barriers so that the calculated path will go around the defined barriers. The nodes and arcs are spatially indexed. This enables live reverse geocoding as the mouse moves - that means that once the mouse is close enough to a street segment, a snap point is calculated and based on the side of the street, an interpolated address is calculated and displayed. Check out the application here, make sure to zoom in to enable snapping and path finding. Again, once you move close enough to a street segment, a snap point is displayed and the address is displayed on the top right corner. You can type the 'x' character to toggle the street as a barrier. Upon a mouse click, the closest node is selected as a starting node. Next, move the mouse to another node and click, and a path is displayed. The last selected node is now the starting node, click again and a path is calculated and displayed. Type 'C' to clear the resulting path. Like usual the source code is here. Have fun.
PS: People have reported some drawing issues :-( that have been resolved if you use FP10.

Friday, April 10, 2009

GoogleAppEngine and BlazeDS

Google released a java version of the AppEngine. So I was very excited and wanted to see if I can host an application based on the Spring BlazeDS Integration framework. All was fine until I deployed the application and got a "Class Not Found" Exception. Looking at the log found the following: java.lang.management.ManagementFactory is a restricted class. Please see the Google App Engine developer's guide for more details. Check the docs and there is a list of "white-list" classes, and java.lang.management is not there :-(. Was so close! oh well, back to EC2 :-)

Sunday, February 22, 2009

Line Style With Arrows

A colleague of mine asked me if there was a way to put arrows at the end of drawn polylines. My answer was "Not out of the box directly :-( but you can create your own line style subclass :-) And here is an implementation in action. Check out the source code here.

Thursday, February 19, 2009

Yet Another Heat Map


While in Redlands last week, I was working with a client on how to create a heat map of an annual precipitation data over the US. He had about 11.5K map points with precipitation values and wanted to render this data very efficiently on the client browser using the new Flex API and wanted to have control over the rendering criteria. We loaded the data onto SqlServer and fronted it with WebORB (since he was a .Net dude, I'm a Java dude and would have used BlazeDS) to enable RemoteObject interaction. The idea here is to bulk load the data using SQL onto some structure and AMF it to the client. On the client side, we take advantage of that structure and use the Flash graphic primitives to render it. Turn out the most efficient structure is composed of 3 arrays holding the x, y and precipitation values. Since on the client we did not need to interact with each value, I created a custom layer that sub-classes Layer to render the 11 thousand values on a bitmap, and then the bitmap is rendered as a layer. Each value was rendered as a rectangle whose size varied based on the map scale and was filled with a color proportional to its value. Actually, we did modify the code a bit to have interaction with the values as the mouse hovers over an area (post for another day :-) We added a slider with two thumbs on top of the map to restrict the rendering of the precipitation values to the range between the thumbs, all client side. We enabled live dragging and the result was very cool. Check it out for yourself here. The version that you are looking at downloads the 11K map points from a shapefile (exercise for the reader to make it an RO :-) and like usual you can download the source code from here. Thanks Eric for sharing the data and code snippets.

Monday, February 9, 2009

Accepted At 360|Flex Indy

I just got accepted to speak at 360|Flex Indy! The show will be going down May 18-20, 2009. You can check out the other great 49 speakers on the schedule at: http://360conferences.com/360flex/downloads/schedule.pdf It's looking like it's gonna be another great 360|Flex conference, especially with yours truly speaking. Tickets are cheaper on a first come, first serve basis! So buy your tickets asap at http://360flex.eventbrite.com to get the best possible price. See you there and you better go to my session!

Ground Overlays On Maps


Found a historic map of Boston collected by David Rumsey, and thought it would be nice to be able to put this map on a current map. As the image needs to be scaled, rotated and translated, I wrote a custom symbol (OverlaySymbol) to enable the scaling, rotation and translation when the map extent changes. So all that is cool, but the problem was coming up with OverlaySymbol properties. So I wrote the OverlayEditor application that enables you to load the image and using a instance of Flex Object Handles, you can stretch, move and rotate the image. Once you are satisfied with the parameters, you can view the source code of a sample application that you can save and compile with a reference to the OverlaySymbol.as code. You can check out the application here. You can load an image of Mount Etna in Sicily. And like usual you can download the source code from here.

Friday, January 23, 2009

[ANN] Flex Mapping API For AGS Ver 1.1 Released

I'm very proud to announce that we just released the 1.1 version of the Flex API For ArcGIS Server. Check it out, and tell us what you think. Happy mapping all.

Thursday, January 22, 2009

Constraining Map Extent

Though sometimes an ArcGIS map service extends the full world, an application that uses this map service will want to restrict the extent to a way smaller extent. In this post, I will be talking about how to achieve this. Check out the application in action. Note that when you pan the map, it "springs" back to the initial extent. And even after you zoom in, the spring is still in effect. So how did I do this is as follows: I created a sub class of the Map class. In the constructor, I added a LOAD event listener. The LOAD event handler adds two EXTENT_CHANGE event listeners, where one of them is at a lower priority to guarantee that it will called last. This low priority listener sole purpose is to get and save the map extent as the initial extent. In addition, the initial scale is computed and saved too. Now, upon an extent change, we check if the initial extent intersects the new extent. if it does, then we compute the horizontal and vertical overlap distance and we adjust the extent in such a way that there is not overlap. we get the "spring" effect for free (cool, eh?) by invoking a callLater. The callLater handler sets a "bounded" extent, thus the spring effect. Now one last thing, we have to make sure we do not zoom out beyond the initial extent. we accomplish this by overriding the extent setter function, where we convert the given extent to a scale value and we compare that to the initial scale. If it is smaller, we proceed by calling the super function, otherwise we just return. Hope you find this post useful. And like usual you can download the source code from here.

Wednesday, January 21, 2009

Collaborative Mapping using Cocomo

In this post I'm demonstrating how to use Cocomo's data messaging to enable collaborative mapping. One or more map application can share their active extent and whiteboard using the mouse. Though the application is not very sophisticated, it demonstrates the extensibility of the flex api and the power of Cocomo. BTW, I highly recommend that you read Ryan's blog post on how to get started with Cocomo. Using the CocomoDevConsole, I created a CollectionNode and named it 'cocomoMap', and I added two non-persistant Nodes. The application uses the node 'cocomoMapExtent' to publish and receive active map extents, and the node 'cocomoMapPoints' is used to publish and receive drawn polylines. One of the cool things that I like in Cocomo, is that you can register a class with the MessageItem registry to enable the publishing and receiving of message bodies in 'native' format. So, I registered the class SpatialReference, MapPoint, Polyline and Extent in such a way that I can directly publish extents and polylines without having to come up with my own serializing/de-serialization scheme. Like usual, you can download the source code from here, and make sure to compile the application with the following switches '-keep-as3-metadata+=Inject -keep-as3-metadata+=Listen'. The later is a post for another day.

Saturday, January 17, 2009

360|Flex - Need Your Vote

I will be speaking at 360|Flex this year in Indianapolis. But this time, it was decided to let the community vote on subjects/speakers. So...I'm asking for your vote. The Title of the session is Yet Another Flex Mapping API - NOT! where we will demonstrate the Flex Mapping API For ArcGIS Server, where we will deconstruct live real-world mapping applications and algorithms such as dense clustering, auto labeling of features, collaborative editing, real-time asset tracking and client/server collaborative Geo-Processing. In addition, we will demonstrate how to create your own layer, geometry and symbol extensions for superior rendering performance taking advantage of the FP10 drawing API. Vote here and vote often.

Sunday, January 4, 2009

Map Point Label Placement

In this post, I will be demonstrating how to label map points using the flex API for AGS. This work is strongly based on Kevin Mote’s “Fast Point-Feature Label Placement For Dynamic Visualization”. In this application, I’m loading city data from a shapefile. The city attributes contain the city name and rank. Rank is a number between 1 and 7 where 1 indicates a very populated city and 7 indicates a much lesser populated city. Upon a map load event, the shapefile is read and the cities with a rank less or equal to 3 are selected then sorted in descending rank order. Each shapefile record is converted into a LabelGraphic and added to a list. A LabelGraphic is a subclass of Graphic and has a TextField as a child. A ExtentChange event listener is added to the map, in such a way that whenever the map extent changes, a LabelManager is created to manage the LabelGraphics in the visible extent. A LabelManager has a spatial index (a trellis per Mote’s) to quickly locate label candidates (more on this later) and has a list of Features in ascending priority. LabelGraphics should be added in incremental order of priority to the label manager. Each added LabelGraphic is wrapped with a Feature from which, four label candidates are derived. The label of a feature can reside in one of the four corners (candidate) around the feature; upper-right, lower-right, upper-left, lower-left. The later order is an “aesthetic” priority per Hartmann et al as discussed in Mote’s paper. Once all the LabelGraphics are added, the solve function is invoked. There, the “cost” of each feature is calculated in such a way that higher priority features are greatly weighted compare to lower priority features (Mote’s cost formula is applied here) In addition, in that step any feature candidate that is not fully visible on the map is removed. The features are in a stack and are popped one at the time, and for each feature candidate, we find all the overlapping candidates from the other features (the spatial index or the trellis comes very handy here :-) and we calculate the cost of that candidate based on the sum of the overlapping candidate. The candidate with the least cost sum is selected to label that feature and the TextField associated with the parent’s feature LabelGraphic is repositioned to “properly” label the feature upon the update of the display list. The other candidates in that feature are removed and the candidates from the other features that overlap the selected candidate are removed as feature label candidates. Now, something very important; the cost of the removed candidate is added to the feature in such a way that it makes is more costly to remove a candidate of a feature with fewer and fewer candidates. Due to this process of candidate elimination, lower priority features could not be labeled, but that is ok; that is why they are at a lower priority :-) There are differences between Mote’s “Trellis Strategy” and this implementation; In the Mote’s approach each feature is optimized for a uniformly sized labels where in this implementation each feature can be labeled with its own text format. In addition, the trellis is reconstructed on each extent change based on visible features, eliminating the cost adjustment based on distance. And finally, the trellis cell size is fixed and proportional to the map pixel width and height. You can see the application in action here, and like usual you can download the source code from here.