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.

11 comments:

dje said...

Hi
I can't compile your code because it needs to import com.esri.ags.geometry.rtree.IRTreeObject but this class is not present on AGS lib 1.0. Do you use an newer version of this lib ? Is it this lib that allows you to have a fade effect on map extent event ?
Thanks for your post
Jeremy

thunderhead said...

Sorry about that - I've uploaded the latest src - thanks for catching it !

Kevin said...

Great work Mansour! It's gratifying to see my efforts implemented on the web. (I never had the opportunity to take it that far.) I'd love to see it with a denser dataset. Thanks for putting it up.

eric said...

Very cool, Mansour.
Amusing, though, that the client-side spatial data structure is first demonstrated for, um, labeling... LOL.
Keep it up, and thank you.

Unknown said...

Hey,
I've been browsing in your blog and it's very helpful, thanks.
I have couple of questions
1. What will be the best method to mark a symbol on the map. Only one can be selected at each time. I need buttons placed around the symbol. Can I change completely the look of the info window (remove the handle)?
2. If I add items to the graphics layer while changing map zoom or doing pan, the symbols are located in the wrong place. Any way to avoid that?
thanks

thunderhead said...

Glad you like it :-) Yes - you can change the skin and L&F of the info window - check out the online samples - In addition, have done some skinning using degrafa. As to the second question - will double check it on my end - would be better to add an extent change listener and wait for the extent change to add items. in addition, u can monitor the start and end of the zoom by addition zoom event listeners. hope that helps.

suraj said...

Hi,

I want to show more cities in MAP.
e.g now as per application only two to four cities displaying.

I want to show maximum cities.Is this possible?

Thanks in advance

thunderhead said...

Not sure I understand the question....I render as much as possible labels that do not overlap

Me said...

How can we integrate ARCGIS maps with Ravis Birds eye's Visualgraph ? My intension is to place the ravis visual graph over the map and the nodes of the visual graph must be placed on their specific location on the map ...

Can anybody help me in this regard ?

Thanks in advance .

Me said...

Or else ..how can we add visual graph to a ESRI Map ...

thunderhead said...

The fastest and easiest way is to place point graphics on the map and join them with line graphics.