Friday, May 29, 2009
Direct use of Polygon Shape File in Layer for ESRI
So I went to the Flex 360 Conference and saw some great presentations.
But the two I liked the most were the ESRI mapping session by Mansour
Raad and the Array's one by Michael Labriola. Mansour talked about
building a custom layer that used sprites for the graphics and a spatial
index. Then in the Array's session Michael talked about how to improve
performance by using byte array's and vectors. So I combined both and
have a custom shapefile layer that never converts the data from the
raw byte array and I use the spatial index to speed the draw times also.
It needs some work still but it works pretty well already and is pretty
easy. So here is the code and since I am using vectors you will need flash player 10.
Tuesday, April 14, 2009
Function Pointers to parse XML/KML
So I wrote a XML Parser for KML. I decided to try doing it using
function poiters because of the results of earlier testing I had done.
In the test the function pointers were faster as both the number
of possible xml tags increased and the total number of tags increased.
You also end up with generic functions that do most of your work (ie
parseInt, parseString, parseHexNumber and so on) which makes the code
much more extensible.
The way this code works requires some up front work in the constructor.
First we have to create the function pointers object.
As you can see the kml object has three possible children: Document, Folder, and Placemark. Each of these have a func(Function) assigned to them. So if we are in the kml tag and find a Document the this.parseXML function will be executed. This parseXML function looks like this:
If you notice there are two other variables in the this._validParseKids object, attrName and attrType. Here is an example of both:
So the attrName is the name of the object that any returned values from the function that was called may return. The attrType is only used to say that there will be multiple objects that return data to the given attrName. So for example the when in the ExtendedData tag you can have an unlimited number of Data tags that are to be stored in an array. As a final example here are a couple of the basic parse functions:
this._validParseKids.kml = { Document : { func: this.parseXML } , Folder : { func : this.parseFolder } , Placemark : { func : this.parsePlacemark } }; this._validParseKids.Document = { Folder : { func : this.parseFolder } , Placemark : { func : this.parsePlacemark } , Style: { func : this.parseStyle } , StyleMap: { func : this.parseStyleMap } }; this._validParseKids.Folder = { Folder : { func : this.parseFolder } , Placemark : { func : this.parsePlacemark } };
As you can see the kml object has three possible children: Document, Folder, and Placemark. Each of these have a func(Function) assigned to them. So if we are in the kml tag and find a Document the this.parseXML function will be executed. This parseXML function looks like this:
protected function parseXML( x:XML ) : Object { var returnObject:Object = new Object(); var validKids:Object = this._validParseKids[x.name().localName]; if( validKids != null ){ var kids:XMLList = x.children() for each( var node:XML in kids ){ var validKid:Object = validKids[node.name().localName]; if( validKid != null && validKid.func is Function ){ validKid.attrName = ( validKid.attrName == null ) ? node.name().localName : validKid.attrName; if( validKid.attrType == "Array" ){ if( returnObject[validKid.attrName] is Array == false ){ returnObject[validKid.attrName] = new Array(); } returnObject[validKid.attrName].push( validKid.func( node ) ); } else{ returnObject[validKid.attrName] = validKid.func( node ); } } } } else{ returnObject = "Not Found in Valid Kids Object"; } return returnObject; }
If you notice there are two other variables in the this._validParseKids object, attrName and attrType. Here is an example of both:
this._validParseKids.Placemark = { Point : { attrName: "_shape" , func : this.parsePoint} , LineString : { attrName: "_shape" , func : this.parseLineString } , LinearRing : { attrName: "_shape" , func : this.parseLinearRing } , Polygon : { attrName: "_shape" , func : this.parsePolygon } , name : { attrName: "name" , func : this.parseString } , address : { attrName: "address" , func : this.parseString } , phoneNumber : { attrName: "phoneNumber", func : this.parseString } , Snippet : { attrName: "Snippet" , func : this.parseString } , description : { attrName: "description", func : this.parseString } , styleUrl : { attrName: "_style" , func : this.parseStyleUrl } , ExtendedData : { attrName: "_data" , func : this.parseExtendedData } }; this._validParseKids.ExtendedData = { Data : { attrName : "kvPairs" , attrType : "Array" , func : this.parseData } };
So the attrName is the name of the object that any returned values from the function that was called may return. The attrType is only used to say that there will be multiple objects that return data to the given attrName. So for example the when in the ExtendedData tag you can have an unlimited number of Data tags that are to be stored in an array. As a final example here are a couple of the basic parse functions:
protected function parseString( text:XML ) : String { return text.toString(); } protected function parseInt( i:XML ) : int { var s:String = i.toString(); var n:Number = Number( s ); if( isNaN( n ) ){ n = this.stringToHex( s ); } return int( n ); } protected function parseNumber( n:XML ) : Number { return Number( n.toString() ); } protected function parseBoolean( b:XML ) : Boolean { var value:String = b.toString(); if( value.match( /(1|true)/ ) ){ return true; } return false; }
Labels:
ActionScript,
AS,
Flex,
Function Pointers,
KML,
parse
Friday, March 6, 2009
ESRI vs. Google, Flex API Load Times Compared. Part 2
In an effort to be fair I decided to rerun the test I had done earlier, but this time using polygons. Part of the reasoning is that I feel that Google's Marker has a lot of crap that it has to draw, like the shadow and just the balloon shape alone would add time. The polygon in Google is just that a polygon and nothing extra. So I created 2000 different bounding boxes and let both ESRI and Google load them. As suspected Google's performance was much better but ESRI was still faster.
So the moral of the story is don't use Google's Marker if you want to go fast.
ESRI | |
---|---|
3.828 sec. | 3.032 sec. |
3.891 sec. | 2.875 sec. |
3.953 sec. | 2.969 sec. |
So the moral of the story is don't use Google's Marker if you want to go fast.
Thursday, March 5, 2009
Try catch vs if statements
Many might already know this but I wanted to know for sure. Is it faster to use a try catch block or if statement. So I ran a simple test to find out. I created this function to run the try catch test;
I then wrote an identical function except that it had an if( o[y] != null ) statement where the try was. Now what I found was that the if statement times did not change it took 0.57 seconds in all the tests. However, the try catch statements did not preform quite as well. As the number of exceptions are thrown the amount of time increases rapidly. Here is a table of times for the percentage of exceptions thrown over a million iterations.
I ran the function without the try catch/if statement and it took 0.422 seconds. If I subtract that from the times and then divide the number of exceptions thrown by the time you end up about 90,000 for each of the times. To put it another way, on my computer I can throw 90,000 exceptions per second.
So it seems like unless you are really do not like if statements it would be faster to use them.
private function testTryCatch() : void { var adder:Function = function a( x: int ) : int { return (x + 1); }; var x:int = 0; var o:Object = { 0 : adder }; for( var i:int = 0; i < 1000000; i++ ){ var y:int = i % 2; try{ x = o[y]( x ); } catch( except:Object ){ x = adder(x); } } }
I then wrote an identical function except that it had an if( o[y] != null ) statement where the try was. Now what I found was that the if statement times did not change it took 0.57 seconds in all the tests. However, the try catch statements did not preform quite as well. As the number of exceptions are thrown the amount of time increases rapidly. Here is a table of times for the percentage of exceptions thrown over a million iterations.
0% | 33% | 50% | 90% |
---|---|---|---|
0.5 | 4.219 | 6.031 | 10.422 |
I ran the function without the try catch/if statement and it took 0.422 seconds. If I subtract that from the times and then divide the number of exceptions thrown by the time you end up about 90,000 for each of the times. To put it another way, on my computer I can throw 90,000 exceptions per second.
So it seems like unless you are really do not like if statements it would be faster to use them.
Wednesday, March 4, 2009
ESRI vs. Google, Flex API Load Times Compared
I have been writing a set of several parsers (Shapefile, Shapefile DBF, KML, CSV, and Tab Delimited) that work for both ESRI and Google. The parsers format the data and pass it to an overlays factory class. The factory class has the map you are working with and also a set of default shape builder classes, that know how to build the appropriate shape for your map. This is the setup that I used to test the speed of loading points into both ESRI and Google maps.
First, I tested loading a kml with 1297 points. To get a baseline I commented out the call to the factory class and got an average parse time of 0.178. I have subtracted this time out of the times below.
Google's default Marker is slower, so in all fairness I set the marker options to { hasShadow: false , radius: 1 , icon : "none" }, this dramatically increased the load times for google.
As you can see the default ESRI point is considerably faster (8-9 times) than the default Google point. If you compare more the ESRI point to the modified Google point it is still faster (3-4 times).
I then ran the same tests for loading a shapefile with 1297 points. The baseline time for the shpfile parser was 0.11 (however the dbf took 4.01 seconds) and has been subtracted out of the times below.
The times are essentially the same as those in the last test. One interesting note though is that reading data from the dbf file makes using a shapefile prohibitevly slow as the amount of data grows.
So I would suggest parsing kml and putting it on a ESRI map for your best results.
First, I tested loading a kml with 1297 points. To get a baseline I commented out the call to the factory class and got an average parse time of 0.178. I have subtracted this time out of the times below.
Google's default Marker is slower, so in all fairness I set the marker options to { hasShadow: false , radius: 1 , icon : "none" }, this dramatically increased the load times for google.
Default Google | Modified Google | ESRI |
---|---|---|
3.697 | 1.462 | 0.416 |
3.651 | 1.447 | 0.384 |
3.665 | 1.462 | 0.385 |
As you can see the default ESRI point is considerably faster (8-9 times) than the default Google point. If you compare more the ESRI point to the modified Google point it is still faster (3-4 times).
I then ran the same tests for loading a shapefile with 1297 points. The baseline time for the shpfile parser was 0.11 (however the dbf took 4.01 seconds) and has been subtracted out of the times below.
Default Google | Modified Google | ESRI |
---|---|---|
3.947 | 1.703 | 0.562 |
3.838 | 1.577 | 0.468 |
3.796 | 1.656 | 0.453 |
The times are essentially the same as those in the last test. One interesting note though is that reading data from the dbf file makes using a shapefile prohibitevly slow as the amount of data grows.
So I would suggest parsing kml and putting it on a ESRI map for your best results.
Monday, March 2, 2009
Parsing KML with ActionScript XML e4x
I spent some time trying to figure out why I could not get the ActionScript XML object to properly parse a KML file and found out that the problem was in the namespace of the KML. KML sets its global namespace to whichever version of KML
you are using. The problem is that when you try to access the tags using the e4x methods it does not work. So to fix the problem you need to then set the default namespace.
default xml namespace = new Namespace("http://www.opengis.net/kml/2.2");
Thursday, February 26, 2009
Which is faster in ActionScript if, switch or object of function pointers
In an effort to parse a kml file as quickly as possible, I need to know if it was
faster to use if statements, switch statements or an object with each tag as a key to a function pointer. I created a test function for each of these methods. I then timed each of the test functions, as I iterated it 1000, 10,000, 100,000, and 1,000,000 times. The results show that the object wiith function pointers is by far the fastest when you have lots of iterations. There seems to be some start up cost
associated with creating the object and the functions. Here is a table of the results, as you can see the results from 10,000 on are all very linear.
So I started off with the if statements:
Next I looked at the switch statements.
Finally I created a function for the object of function pointers.
iterations | if statements | switch statements | function pointers |
---|---|---|---|
1000 | 0.015 | 0.031 | 0.032 |
10,000 | 0.218 | 0.25 | 0.141 |
100,000 | 2.297 | 2.563 | 1.343 |
1,000,000 | 22.813 | 26.313 | 13.203 |
So I started off with the if statements:
private function testIfStatements( names:Array , iterations:int ) : int { var x:int = 0; for( var i:int = 0; i < iterations; i++ ){ for( var j:int = 0; j < names.length; j++ ){ if( names[j] == 'amy' ){ x = x + 1; } else if( names[j] == 'bob' ){ x = x + 1; } else if( names[j] == 'curly' ){ x = x + 1; } else if( names[j] == 'dawn' ){ x = x + 1; } else if( names[j] == 'erick' ){ x = x + 1; } else if( names[j] == 'fred' ){ x = x + 1; } else if( names[j] == 'george' ){ x = x + 1; } else if( names[j] == 'harley' ){ x = x + 1; } else if( names[j] == 'ian' ){ x = x + 1; } else if( names[j] == 'jim' ){ x = x + 1; } else if( names[j] == 'kim' ){ x = x + 1; } else if( names[j] == 'liam' ){ x = x + 1; } else if( names[j] == 'moe' ){ x = x + 1; } else if( names[j] == 'nate' ){ x = x + 1; } else if( names[j] == 'oliver' ){ x = x + 1; } else if( names[j] == 'penelope' ){ x = x + 1; } else if( names[j] == 'quinn' ){ x = x + 1; } else if( names[j] == 'robb' ){ x = x + 1; } else if( names[j] == 'silas' ){ x = x + 1; } else if( names[j] == 'tim' ){ x = x + 1; } else if( names[j] == 'ulysses' ){ x = x + 1; } else if( names[j] == 'victor' ){ x = x + 1; } else if( names[j] == 'waldo' ){ x = x + 1; } else if( names[j] == 'xavier' ){ x = x + 1; } else if( names[j] == 'yolanda' ){ x = x + 1; } else if( names[j] == 'zidian' ){ x = x + 1; } } } return x; }
Next I looked at the switch statements.
private function testSwitchStatements( names:Array , iterations:int ) : int { var x:int = 0; for( var i:int = 0; i < iterations; i++ ){ for( var j:int = 0; j < names.length; j++ ){ switch(names[j]){ case 'amy': x = x + 1; break; case 'bob': x = x + 1; break; case 'curly' : x = x + 1; break; case 'dawn' : x = x + 1; break; case 'erick' : x = x + 1; break; case 'fred' : x = x + 1; break; case 'george' : x = x + 1; break; case 'harley' : x = x + 1; break; case 'ian' : x = x + 1; break; case 'jim' : x = x + 1; break; case 'kim' : x = x + 1; break; case 'liam' : x = x + 1; break; case 'moe' : x = x + 1; break; case 'nate' : x = x + 1; break; case 'oliver' : x = x + 1; break; case 'penelope' : x = x + 1; break; case 'quinn' : x = x + 1; break; case 'robb' : x = x + 1; break; case 'silas' : x = x + 1; break; case 'tim' : x = x + 1; break; case 'ulysses' : x = x + 1; break; case 'victor': x = x + 1; break; case 'waldo' : x = x + 1; break; case 'xavier' : x = x + 1; break; case 'yolanda' : x = x + 1; break; case 'zidian' : x = x + 1; break; default : } } } return x; }
Finally I created a function for the object of function pointers.
private function testFunctionPtrs( names:Array , iterations:int ) : int { var functions:Object = new Object(); functions['amy'] = function a( y:int ) : int { return (y+1); }; functions['bob'] = function b( y:int ) : int { return (y+1); }; functions['curly'] = function c( y:int ) : int { return (y+1); }; functions['dawn'] = function d( y:int ) : int { return (y+1); }; functions['erick'] = function e( y:int ) : int { return (y+1); }; functions['fred'] = function f( y:int ) : int { return (y+1); }; functions['george'] = function g( y:int ) : int { return (y+1); }; functions['harley'] = function h( y:int ) : int { return (y+1); }; functions['ian'] = function i( y:int ) : int { return (y+1); }; functions['jim'] = function j( y:int ) : int { return (y+1); }; functions['kim'] = function k( y:int ) : int { return (y+1); }; functions['liam'] = function l( y:int ) : int { return (y+1); }; functions['moe'] = function m( y:int ) : int { return (y+1); }; functions['nate'] = function n( y:int ) : int { return (y+1); }; functions['oliver'] = function o( y:int ) : int { return (y+1); }; functions['penelope'] = function p( y:int ) : int { return (y+1); }; functions['quinn'] = function q( y:int ) : int { return (y+1); }; functions['robb'] = function r( y:int ) : int { return (y+1); }; functions['silas'] = function s( y:int ) : int { return (y+1); }; functions['tim'] = function t( y:int ) : int { return (y+1); }; functions['ulysses'] = function u( y:int ) : int { return (y+1); }; functions['victor'] = function v( y:int ) : int { return (y+1); }; functions['waldo'] = function w( y:int ) : int { return (y+1); }; functions['xavier'] = function X( y:int ) : int { return (y+1); }; functions['yolanda'] = function Y( y:int ) : int { return (y+1); }; functions['zidian'] = function Z( y:int ) : int { return (y+1); }; var x:int = 0; for( var i:int = 0; i < iterations; i++ ){ for( var j:int = 0; j < names.length; j++ ){ x = functions[names[j]](x); } } return x; }
Labels:
ActionScript,
faster,
fastest,
function,
if statement,
object,
pointers,
switch statement,
test
Tuesday, February 24, 2009
Add Image overlay to ESRI Map in FLEX
Adding an image overlay to an ESRI Map in Flex is not really a default
functionality. So when a project came along that required one I wrote
this. There does seem to be a size limitation for the image you are
trying to load. I did not really have a chance to explore the issue,
but it worked when the image width was under 2500 pixels wide.
package com.cinque_fiori.mapping.symbols { 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; import flash.display.BitmapData; import flash.geom.Matrix; use namespace esri_internal; public class BitmapMarkerSymbol extends Symbol { private var _bitmap:BitmapData; private var _lowerLeft:MapPoint; private var _visible:Boolean; public function BitmapMarkerSymbol () { super(); this._lowerLeft = new MapPoint(); this._bitmap; this._visible = true; } override esri_internal function drawGraphic( map : Map, graphic : Graphic ): void { if( graphic.geometry is MapPoint ) { drawBitmapOnMap( map, graphic, MapPoint( graphic.geometry )); } } private function drawBitmapOnMap( map : Map, graphic:Graphic,mapPoint:MapPoint ): void { if( this._lowerLeft != null && this._bitmap != null ){ graphic.x = map.mapToContainerX(mapPoint.x); graphic.y = map.mapToContainerY(mapPoint.y); var w:Number = map.mapToContainerX(this._lowerLeft.x) - graphic.x; var h:Number = map.mapToContainerY(this._lowerLeft.y) - graphic.y; var m:Matrix = new Matrix(w/this._bitmap.width,0,0,h/this._bitmap.height,0,0); graphic.graphics.clear(); if(this._visible){ graphic.graphics.beginBitmapFill( this._bitmap, m , false , true ); graphic.graphics.drawRect(0,0,w,h); graphic.graphics.endFill(); } } } public function get lowerLeft() : MapPoint{ return this._lowerLeft; } public function set lowerLeft( pt:MapPoint ) : void{ this._lowerLeft = pt; } public function get bitmap() : BitmapData{ return this._bitmap; } public function set bitmap( bitmapData:BitmapData ) : void{ this._bitmap = bitmapData; } public function get visible() : Boolean{ return this._visible; } public function set visible( visible:Boolean ) : void{ this._visible = visible; } } }
Labels:
ActionScript,
ESRI,
Flex,
Image,
Map,
Mapping AS,
Overlay
Subscribe to:
Posts (Atom)