Monday, December 1, 2008

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.

16 comments:

Prem said...

Very Cool, I will be checking this out soon. I was waiting for this after I talked to you about this at MAX.

Unknown said...

Mansour, what happened to Ireland?

thunderhead said...

No disrespect intended to the countries and islands that are missing from the shapefile. Found a simplified continent file and used it as a sample. The focus was more on the flex api than the data :-)

Prem said...

Mansour, seems like the source is not available anymore. Could you please fix that?
Thanks
Prem

thunderhead said...

Power is out on my server - working in it !

Unknown said...
This comment has been removed by the author.
Unknown said...

It is a neat tool. I wanted to use a different shape file, but got nothing displayed in the flash. Are there any limitations in using this function, in terms of projections and file sizes? Thanks!

thunderhead said...

Glad you like it - not aware of any limitations - what type of shapefile are u rendering - points, line, poly ?

Unknown said...

I tried several polygon files. It seems the Viewer only works for Geographic Coordinate Systems(longtitude/latitude), not the projected Shape files. Another thing is that the Viewer was set to view the whole world, so when loading a local map, it is hard to see it from the initial view.

thunderhead said...

Ah yes - that is the default SpatialReference - u can do it in mercator - will post something :-)

Bouiaw said...

We should be interested to integrate Shapefile support in OpenScales (http://openscales.org/)

ncarolina.marc said...

Mansour,

Is there anyway possible to have done the same thing with a local mxd and pgdb?

We are wanting to use FlexViewer to work with local maps (as well as server maps).

Is this possible?

Thanks!

thunderhead said...

Give that all is done using a ByteArray - then all is possible :-)

SY said...

Mansour: Very interesting topic. However, I tried but not successul. Could you provide more detail (a go-through procedure) for it? Thanks.

thunderhead said...

Look @ http://thunderheadxpler.blogspot.com/2009/09/map-layer-from-local-shapefile.html

SY said...

Dear Mansour:
Thanks for your response. I am a new user in Flex. I tried again but still was unsuccessful. Please see my procedure below:


1) Open Adobe Flex Builder 3 and create a Flex Project (in C:\...\MyFlexProject).
2) Add ArcGIS API for Flex library (swc file).
3) Copy the ShpViewer.mxml and paste it into C:\...\MyFlexProject \src\.
4) It creates MyFlexGIS2.mxml file under C:\...\MyFlexProject \src\.
5) Copy and paste your code to replace the generated code in the created .mxml file:.com/2006/mxml"
6) Copy the two folders of yours: \assets\ (it contains ContinentsWithLabels.dbf and ContinentsWithLabels.shp) and \com\ (it contains \com\esri\) from original source (unzipped from your ShpViewer) into C:\...\MyFlexProject \src\.
7) Run the created .mxml but got an error: File cannot be found: file:/C:/…/MyFlexProject/bin-debug/MyFlexProject.html.

Please provide your advise. Thanks.