This tutorial is the second part of a tutorial series about customized map projection in flash/actionscript. It shows how to import a shapefile into ActionScript via the SHP library by Edwin van Rijkom.
1 Reading Shapefiles in PHP, 2 Reading Shapefiles in ActionScript.
So, after Zachary Forest Johnson told me that he changed is mind about server-side reading of shapefiles, I decided to create another tutorial showing the client-side method using Edwin van Rijkom’s SHP library.
Step 1: Downloading the shapefiles
This is obviosly exactly the same as in the first tutorial: We start by downloading a shapefile from finder.geocommons.com which is a large portal for free map datasets.
I decided to use the dataset World Countries Boundary File, World, 2002 because it includes not only information about the country boundaries but also some useful meta information like ISO codes etc. Here is a direct link to the zip-file containing the shapefiles.
The zip-package contains three files, one shp-file containing all shape coordinates, one dbf-file containing all shape meta data and one shx-file containing the shape index file. You find more information on the structure shapefiles in the wikipedia.
Step 2: Importing the shapefiles into flash
At first we have to load the shapefile data into the flash movie. I chose to do this using the excellent BulkLoader library, but you can also do this by hand by using the native URLLoader class instead.
var b:BulkLoader = new BulkLoader(BulkLoader.getUniqueName()); b.add('5961.dbf', { id: 'dbf', type: 'binary' }); b.add('5961.shp', { id: 'shp', type: 'binary' }); b.add('5961.shx', { id: 'shx', type: 'binary' }); b.addEventListener(BulkProgressEvent.COMPLETE, run); b.start();
This is the part where Edwin van Rijkom’s SHP library comes into the game. I first tried to extract the shape records using the class ShpTools:
private function run(e:BulkProgressEvent):void { var shp:ByteArray, bl:BulkLoader = e.target as BulkLoader shp = bl.getBinary('shp'); trace(ShpTools.readRecords(shp)); }
This attempt resulted in an exception. After a while I figured out that one has to create an instance of ShpHeader before the parsing of the records works.
private function run(e:BulkProgressEvent):void { var shp:ByteArray, bl:BulkLoader = e.target as BulkLoader shp = bl.getBinary('shp'); // we must create a shape header first! var head:ShpHeader = new ShpHeader(shp); trace(ShpTools.readRecords(shp)); }
Now we got an array full of ShpRecord instances. Since our shapefile contains only polygons we can access the information by casting the ShpObject instances to ShpPolygon.
var records:Array = ShpTools.readRecords(shp_raw); for each (var record:ShpRecord in records) { if (record.shapeType == ShpType.SHAPE_POLYGON) { var poly:ShpPolygon = record.shape as ShpPolygon; // now we can access the array poly.rings which contains // all coordinates as ShpPoint instances } }
Step 3: Reading the meta information
The access to the shape meta information (which are stored in the dbf file) can be done using the class DbfTools. But just to be sure I created a DbfHeader instance at first.
// bl is our BulkLoader instance from the code examples above var dbf:ByteArray = bl.getBinary('dbf'); var dbfHead:DbfHeader = new DbfHeader(dbf);
We can allocate the right dbf record to a shape record by using the number property. We have to decrease it by one since the getRecord method starts the counting by zero while the counting of number starts by one (seems to be a little inconsistent at this point).
var records:Array = ShpTools.readRecords(shp); for each (var rec:ShpRecord in records) { if (rec.shapeType == ShpType.SHAPE_POLYGON) { var poly:ShpPolygon = rec.shape as ShpPolygon; // accessing dbf information using the record index, decreased by one var dbfRec:DbfRecord = DbfTools.getRecord(dbf, dbfHead, rec.number-1); var meta:Dictionary = dbfRec.values; var coords:String = poly.rings[0][0].x + ',' + poly.rings[0][0].y + ';' + poly.rings[0][1].x + ',' + poly.rings[0][1].y + '...'; trace(meta.ISO_3_CODE, meta.ISO_2_CODE, meta.NAME, coords); } }
So that’s it! The trace output will look something like this:
AFG AF Afghanistan 65.6272,37.3331;65.6469,37.4588;... ALB AL Albania 19.3973,42.3170;19.4697,42.3999;... DZA DZ Algeria -1.2538,32.2147;-1.25,32.3269;... AND AD Andorra 1.7109,42.4734;1.5333,42.4361;... AGO AO Angola 12.0100,-5.0206;12.1663,-4.8958;...
In the next tutorial I’ll show how to project and display a static map out of these coordinates.
