( ! ) Warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'WPtouchPlugin::wptouch_parse_request' was given in C:\Domains\adamestrada.com\wwwroot\wp-includes\plugin.php on line 414
Call Stack
#TimeMemoryFunctionLocation
10.000461176{main}( )..\index.php:0
20.000761464require( 'C:\Domains\adamestrada.com\wwwroot\wp-blog-header.php' )..\index.php:17
30.62132610056wp( )..\wp-blog-header.php:14
40.62132610336WP->main( )..\functions.php:1434
50.62132610440WP->parse_request( )..\classes.php:490
60.63002628920do_action_ref_array( )..\classes.php:293
70.63012630040call_user_func_array ( )..\plugin.php:414
Adam Estrada: Mapping the Planet 7.5 Minutes at a time...

Drupal vs. Wordpress for Geo

2010 February 26
by Adam Estrada

Both Drupal and Wordpress are very popular content publishing platforms. I’ve been dorking around with Wordpress for a while now (this is a Wordpress site) and I was recently able to work on a project based on the Drupal API. This gave me a very good perspective on both platforms…Keep in mind that both are very powerful and very robust web publishing platforms that can do a variety of different things. This post is intended to express my findings with a little geospatial flavour ;-)

Wordpress:

It doesn’t get any easier than Wordpress! With the stack installer, you can literally be up and running in a matter of minutes. The thing I like the most about Wordpress is that there are

Wow! To top it all off, you simply have to search for the plugin or theme want (inside of the WP Admin Panel) and then click on an “Install” button to add it to your instance. Bang! Done! End of story…Well, there are always some configuration options but that depends on what you install.

Again, Wordpress is great if you want to blog or set up a huge social network ala BuddyPress and Wordpress MU. You can set up advanced user roles using Capability Manager and then use those roles to provide restricted access to parts of your site via User Access Manager. Of course there are many more ways to do this, but I’ve used these and they appear to really work well.

Now, what if you want to add custom data and then retriee it from your blog? Hmmm…Yes you can do this by either extending your user profile or maybe by generating custom fields for a form on your site. Getting the data back out is the hard part and honestly, I really don’t know of a plugin to do this. (Insert Help Here!!!)

There are a ton of cool Geospatial Plugins too which is always something I tend to gravitate to first. Here are a couple that really caught my attention.

  • WP Geo – This one is really cool but limited in what it can do. If you want to geocode your blog post and add GeoRSS to your blog, this is the plugin for you.
  • Visitor Maps – This plugin sniffs out the IP Address of your site visitors and then uses an internal database to geocode (approximately of course) and then plot them out on a map that you can monitor in your administration panel.
  • Of course…if you are serious about getting your info out there, make sure to install All in one SEO Pack.

So that’s Wordpress. You can see that you can publish basic information and extend the look and feel of the site with themes and plugins. There are a lot of plugins out there to choose from but the bottom line is that Wordpress is EASY!

Drupal:

At first glimpse, Drupal is a nightmare to manage and maintain. Actually, that is very true! Drupal has it’s own language and intricacies that take a while to get used to. Luckily, I had @hugoestr there to explain it all and believe me…there was a lot to explain. Like Wordpress, there are many plugins (modules in Drupal) and themes to choose from. There certainly are not as many as Wordpress but it’s gotta be close. I found a nice site to browse through to find the modules we needed. Check out the Drupal Modules website for a pretty good list of all the modules that the community has submitted.

Drupal too has a Bitnami stack that you can download but we decided to go with the Acquia stack for our solution. Why you ask? Well, the founder of Drupal is heavily involved with Acquia and has created a stack with what appears to be a pretty comprehensive set of included module. Also, for whatever reason…Microsoft seems to support this stack which is very strange to me ;-)

OK, so Drupal does everything that Wordpress doesn’t. First, Drupal is more of an API or framework than a high-level publishing platform. It’s probably meant more for the develop-type who doesn’t mind getting their hands dirty to learn what is going on. Drupal is based on a series of “nodes” which are actually just pages. There is also a complex but very powerful taxonomy mechanism built in that allows the user to categorize the content.

Searching: The Aquia Stack comes installed with a Lucene API that adds some serious search power to your site. I really like the fact that you can add the “Did you mean” and “More results like this” functionality to you site. It gives it a bit of a Google-esque feel. Solr is installed by default too which will crawl any files attached to a node but setting it up requires that you have a Solr server up and running. There are a handful of other modules out there that will also allow you to search the content in your file attachments too…

CCK and WebForms: Drupal gives you the ability to create your own content types using the Content Construction Kit (CCK). You can create your own forms and extend current ones using the CCK module. Wow! This is awesome! There is even a Web Form module that will let you set up your forms in a wizard-type approach where the pages are related to each other. By using the Conditional Fields module you can add a decision tree to your workflow. Very nice stuff!!!

Views: Once you get your data in to the Drupal data model you will want to get it out, right? Views allow you to create what is literally a view of any content in the database you want. You can return the results in a variety of different formats ranging from whole pages to a simple table.

Now let’s get to the Geo…There are 3 that are a must have

  • Location – Location is sort of like the WP Geo Wordpress plugin I mentioned earlier only it does a heck of a lot more. With this module you can create your own location content type and add it to nodes across your site. This basically allows you to geocode everything in your site…from the users to the individual nodes.
  • GMap – This module works with the other spatially-aware modules on your site by adding the slippy map interface.
  • Geo – I have not had a chance to do anything with this one but it looks like the daddy.

The geo module is the next generation geospatial module for Drupal. Like Location, it provides storage for points, but it also supports lines and polygons.

Why is this useful? Because locations are more than addresses or dots on a map. Windmills in a field, for example, don’t have a postal address for geocoding, but still need their coordinates stored and mapped. And data such as routes, city boundaries, or flight patterns all have storage and search requirements that cannot be expressed by simple dots. Fortunately, there is an open standard for expressing this type of data, and Geo takes advantage of spatially enabled databases (PostGIS and MySQL Spatial) for native storage, which leads to faster, more informational queries.

I would love to hear some feedback from anyone who’s used this module. Spatial Queries by default in your CMS? That is very intriguing to me!

Overview:

Both Drupal and Wordpress are excellent applications. Wordpress is for very entry level users who have something to say while Drupal is a full blown Content Management System. Both have communities full of developers who contribute to making the project better but it looks like Wordpress is a bit ahead of the curve. I suspect that it’s because Wordpress is Caveman easy and even though the plugins may be complicated to develop, the user footprint is huge which is incentive to develop that most ego-starved programmer can’t pass up. Drupal is not any better or any worse…it’s just different. There is also definitely a learning curve so if you’re familiar with Wordpress and want to dive in to Drupal and expect to get it right away, you’re in for a real treat ;-) I would love some feedback on this topic so please hit me up if you’ve got the time…

GmapEZ

2010 February 6
by Adam Estrada

Using GMapEZ

I have been using GMapEZ for a few years now and it continues to impress me by how easy it is to use. The GMapEZ  script completely eliminates the need for any javascript programming which in my opinion is a really good thing. I have used it in the past to add pushpins that are returned form any number of data sources. I like classic ASP so I’ve returned data from an ASP RecordSet or now a days from a .NET DataReader. At any rate, you simply have to read data from the DB and then write out the HTML to represent the structure needed to put pushpins on your map. Here is a C# example of what I am talking about.

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public partial class Pages_mapPanel : System.Web.UI.UserControl
{
    #region Page Props
 
    public String defaultGmapPage = "http://maps.google.com/maps";
    public String defaultPage = ConfigurationSettings.AppSettings["defaultPage"];
 
    #endregion
 
    protected void Page_Load(object sender, EventArgs e)
    {
        try
        {
            string mapPlaceHolder = this.loadMapData();
            this.ctlPlaceHolder.Controls.Add(new LiteralControl(mapPlaceHolder)); 
        }
        catch (Exception ex)
        {
            throw (ex);
        }
    }
 
    #region loadMapData()
    public String loadMapData()
    {
        try
        {
            OracleConnection conn = new OracleConnection();
            String mainConn = ConfigurationManager.ConnectionStrings["MainConnStr"].ConnectionString;
            conn.ConnectionString = mainConn;
            conn.Open();
            OracleCommand cmd = new OracleCommand();
            cmd.CommandType = CommandType.Text;
 
            String query = "SELECT * FROM MYTABLE ORDER BY DATEIN DESC";
            cmd.CommandText = query;
            cmd.Connection = conn;
 
            cmd.ExecuteReader();
            OracleDataReader dr = cmd.ExecuteReader();
            StringBuilder map = new StringBuilder();
            QueryString qs = new QueryString();
 
            while (dr.Read())
            {
                qs.Add("fid", "ms");
                qs.Add("id", dr["ID"].ToString());
 
                    map.Append("<a id=\"" + dr["ID"].ToString() + "\" href=\"" + defaultGmapPage + "?ll=" + dr["LATITUDE"].ToString() + "," + dr["LONGITUDE"].ToString() + "&amp;t=k&amp;hl=en\">GREEN MINI</a>");
                    map.Append("<div title=\"Event\"><font size=\"1\" face=\"Arial, Helvetica, sans-serif\">");
                    map.Append("<strong>Name:</strong> " + dr["NAME"].ToString() + "<br />");
                    map.Append("<strong>Type:</strong> " + dr["TYPE"].ToString() + "<br />");
                    map.Append("<strong>Description:</strong> " + dr["DESC"].ToString() + "<br />");
                    map.Append("<a href=\"" + defaultPage + qs + "\">Go To Main Page...</a>");
                    map.Append("</font></div>");
                    map.Append("<div title=\"Dates\"><font size=\"1\" face=\"Arial, Helvetica, sans-serif\">");
                    map.Append("<strong>Date In:</strong> " + dr["DATEIN"].ToString() + "<br />");
                    map.Append("<strong>Date Out:</strong> " + dr["DATEOUT"].ToString() + "<br />");
                    map.Append("<a href=\"" + defaultPage + qs + "\">Go To Main Page...</a>");
                    map.Append("</font></div>");
                }
            }
 
            conn.Close();
            return map.ToString();
        }
        catch (Exception ex)
        {
            Zekiah.Logging.LoggingManager.WebApplication.Error("A Fatal Error has occured in the Map Control", ex);
            redirectToFailure();
            return null;
        }
    }
    #endregion
 
	#region Failure Notice 
    private void redirectToFailure()
    {
        QueryString qs = new QueryString();
        qs.Add("fid", "ERROR");
        Response.Redirect(defaultPage + qs, false);
    }    
    #endregion 
}

Please note that I am reading data from an Oracle Database and simply returning it as a properly formatted String in HTML format. Each pushpin also has two tabs which can hold extra data including another URL. For more information on what you can do with GMapEZ, check out the examples on the hosted site. You’ll also note that I used a class written by Bobby ReRosa called “QueryString”. What a freaking time saver this thing is! Thanks Bobby…I actually want to write more on how to use it in another blog post so look for that one too.

Oracle Spatial and USER_SDO_GEOM_METADATA

2010 January 15
by Adam Estrada

Oracle is a very powerful database but at the same time VERY difficult to use. I’m guessing that’s why Oracle DBA’s get all the money and women…hehe. Oracle Spatial is also an incredibly powerful tool, if you know how to use it. I’ve talked before about how to get your spatial data in to the Database, but what happens if the database doesn’t like the data? See, in Oracle you must have an SDO_GEOMETRY type to actually perform spatial queries on your data. What they don’t make so apparent is the fact that you must also Insert a record in to the USER_SDO_GEOM_METADATA table (they call it a view) and also create a Spatial Index to perform the query. SHP2SDO will create the Update/Insert statement for USER_SDO_GEOM_METADATA but if you already Inserted the data and don’t have the original you can use the following to Insert in to your table

1
2
3
4
5
6
7
8
9
INSERT INTO USER_SDO_GEOM_METADATA
VALUES(
'EVENTS','GEOM',
    MDSYS.SDO_DIM_ARRAY(   
    MDSYS.SDO_DIM_ELEMENT('X', -180, 180, 0.000000005),
    MDSYS.SDO_DIM_ELEMENT('Y', -90, 90, 0.000000005)
     ),
  4326   -- SRID (reserved for future Spatial releases)
);

Once you’ve Inserted that record you can look at it using the following Query:

1
2
 
SELECT * FROM USER_SDO_GEOM_METADATA

The Update query is simple…

1
2
3
4
5
6
7
UPDATE EVENTS S set S.GEOM.SDO_SRID = 4326
 
UPDATE USER_SDO_GEOM_METADATA SET SRID = 4326 WHERE TABLE_NAME='EVENTS'
 
UPDATE USER_SDO_GEOM_METADATA SET DIMINFO = 
MDSYS.SDO_DIM_ARRAY(MDSYS.SDO_DIM_ELEMENT('X',-180,180,0.000000005),
MDSYS.SDO_DIM_ELEMENT('Y',-90,90,0.000000005)) WHERE TABLE_NAME = 'EVENTS'

If you want to make sure your geometries are valid, you may use Oracle’s ST_IsValid() command.

1
2
 
SELECT s.GEOM.st_isvalid() FROM EVENTS s

Finally, do you want to generate a GEOM column from latitude/longitude fields?

1
2
3
4
5
6
UPDATE EVENTS
SET GEOM = MDSYS.SDO_GEOMETRY(
2001, -- 3-dimensional point or use 2001 for 2-dimensional point
NULL,
MDSYS.SDO_POINT_TYPE(EVENTS.LON, EVENTS.LAT, NULL), -- X,Y,Z
NULL, NULL );

Have fun!

GIS for the “Have Nots”

2009 December 17
by Adam Estrada

A colleague of mine, Marshall Worthey, gave this presentation at GIS in the Rockies earlier this year. Last night, I did it at a GeoDC event at FortiusOne.

For more information on embedding maps from Geocommons in to Wordpress, check out this previous post of mine.

More on ESRI Flex and Config.xml

2009 December 15

More often than not, when building a Flex application that includes a URL that references a map service, you’ll have to swap out that URL from your development environment to production when you go live. The following script/technique is intended to alleviate having to completely rebuild your application during deployment.

Take a look at the following mxml file and note that there are 3 things in particular to note.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
< ?xml version="1.0" encoding="utf-8"?>
<mx :Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:esri="http://www.esri.com/2008/ags"
    layout="absolute"
    initialize="init()"
    styleName="plain"
    >
  </mx><mx :Script>
    < ![CDATA[
    	import mx.collections.XMLListCollection;
    	import mx.collections.ArrayCollection;
        	import com.esri.ags.Graphic;
        	import com.esri.ags.events.MapMouseEvent;        	
        	import com.esri.ags.geometry.MapPoint;
        	import com.esri.ags.geometry.Extent;
            import com.esri.ags.events.ExtentEvent;
            import mx.rpc.events.FaultEvent;
        	import mx.rpc.events.ResultEvent;
            import mx.controls.Alert;
            import flash.external.*;
            import mx.events.FlexEvent;
            import com.esri.ags.layers.TiledMapServiceLayer;
            import mx.controls.Button;
            import mx.controls.Text;
            import com.esri.ags.layers.ArcGISDynamicMapServiceLayer;
            import mx.rpc.http.HTTPService;
 
            private function initXML():void
        	{
        		var configService:HTTPService = new HTTPService();
        		configService.url = "config.xml";
        		configService.resultFormat = "e4x";
        		configService.addEventListener(ResultEvent.RESULT, configResult);
        		configService.addEventListener(FaultEvent.FAULT, configFault);
        		configService.send();
        	}
 
            private function configResult(event:ResultEvent):void
            {
            	var configXML:XML = event.result as XML;
            	var serviceList:XMLList = configXML..service;
            	for (var i:int = 0; i < serviceList.length(); i++)
            	{
             		var serviceName:String = serviceList[i].name;
            		var serviceURL:String = serviceList[i].url;
            		var serviceAlpha:int = serviceList[i].alpha;
            		var serviceVisible:Boolean = serviceList[i].visible;
            		var serviceLayers:ArrayCollection = new ArrayCollection(serviceList[i].layers.split(","));
 
            		var tLayer:ArcGISDynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer(serviceURL); 
            		tLayer.id = serviceName;
            		tLayer.visible = serviceVisible;
            		tLayer.alpha = serviceAlpha;
            		tLayer.visibleLayers = serviceLayers;
            		myMap.addLayer(tLayer);	
            	}          	
            }
 
            private function configFault(event:FaultEvent):void
            {
            	Alert.show(event.fault.faultString);
            }
 
 
            private function layerShowHandler(event:FlexEvent):void
            {
                // update the LODs/zoomslider to use/show the levels for the selected base map
                var tiledLayer:TiledMapServiceLayer = event.target as TiledMapServiceLayer;
                myMap.lods = tiledLayer.tileInfo.lods;
            }
 
			//add a callback for js to flex communication
			public function init():void
			{
				initXML();
			  if (flash.external.ExternalInterface.available)
			    flash.external.ExternalInterface.addCallback("changeExtent", changeExtent);     
			}
 
			//call from js
			public function changeExtent(sextent:String):void	
			{
				//var avals = sextent.split("|");
				//var ext:Extent = new Extent(new Number(avals[0]),new Number(avals[2]),new Number(avals[1]),new Number(avals[3]));
				//ext.spatialReference = myMap.spatialReference;
				//myMap.extent = ext;
			}
 
            public function onBasemapCreationComplete():void
			{
            	myMap.addEventListener(MouseEvent.CLICK, pushExtentEvent);
            }
 
     		//send extents to html/js
     		public function pushExtentEvent(event:MouseEvent):void           
      	 	{
      	 		if (flash.external.ExternalInterface.available)
			     {
			       const mapPoint:MapPoint = myMap.toMapFromStage(event.stageX, event.stageY);
			       var extent:Array = new Array();
			       flash.external.ExternalInterface.call("displayExtent", mapPoint.y.toFixed(6), mapPoint.x.toFixed(6));
			     }        
      	 	}
      	 	private function onMapClick(event:MapMouseEvent):void
      	 	{    	 		
      	 		myMap.infoWindow.label = "Event Location ";
      	 		myMap.infoWindow.show(event.mapPoint);
      	 	}
        ]]>
    </mx>
    <esri :Map id="myMap" width="100%" height="100%" mapClick="onMapClick(event)" >
      </esri><esri :extent>
        </esri><esri :Extent xmin="-122.2" ymin="24.89" xmax="-70.59" ymax="46.92"  >
          <esri :SpatialReference wkid="4326"/>
        </esri>
 
        <esri :ArcGISTiledMapServiceLayer
            visible="{bb.selectedIndex == 0}"
            show="layerShowHandler(event)"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer" 
            creationComplete="onBasemapCreationComplete()" />
        <esri :ArcGISTiledMapServiceLayer
            visible="{bb.selectedIndex == 1}"
            show="layerShowHandler(event)"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer" 
            creationComplete="onBasemapCreationComplete()"/>
        <esri :ArcGISTiledMapServiceLayer
            visible="{bb.selectedIndex == 2}"
            show="layerShowHandler(event)"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer" 
            creationComplete="onBasemapCreationComplete()" />
 
    <mx :ToggleButtonBar id="bb" horizontalGap="2" selectedIndex="0" themeColor="0x00FF00" right="5" top="5">
        </mx><mx :dataProvider>
            </mx><mx :Array>
                </mx><mx :String>Streets</mx>
                <mx :String>Topo</mx>
                <mx :String>Imagery</mx>

First, note the code block that references an external XML file (config.xml). It really doesn’t get any easier than this. What’s really cool about this XML file is that basically, all the web service configuration options from the ArcGIS Service are configured through here. You can traverse through the config.xml file by referencing the node you want in the XMLList or serviceList:XMLList.

var serviceName:String = serviceList[i].name;
var serviceURL:String = serviceList[i].url;
var serviceAlpha:int = serviceList[i].alpha;
var serviceVisible:Boolean = serviceList[i].visible;
var serviceLayers:ArrayCollection = new ArrayCollection(serviceList[i].layers.split(“,”));

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private function initXML():void
        	{
        		var configService:HTTPService = new HTTPService();
        		configService.url = "config.xml";
        		configService.resultFormat = "e4x";
        		configService.addEventListener(ResultEvent.RESULT, configResult);
        		configService.addEventListener(FaultEvent.FAULT, configFault);
        		configService.send();
        	}
 
            private function configResult(event:ResultEvent):void
            {
            	var configXML:XML = event.result as XML;
            	var serviceList:XMLList = configXML..service;
            	for (var i:int = 0; i < serviceList.length(); i++)
            	{
             		var serviceName:String = serviceList[i].name;
            		var serviceURL:String = serviceList[i].url;
            		var serviceAlpha:int = serviceList[i].alpha;
            		var serviceVisible:Boolean = serviceList[i].visible;
            		var serviceLayers:ArrayCollection = new ArrayCollection(serviceList[i].layers.split(","));
 
            		var tLayer:ArcGISDynamicMapServiceLayer = new ArcGISDynamicMapServiceLayer(serviceURL); 
            		tLayer.id = serviceName;
            		tLayer.visible = serviceVisible;
            		tLayer.alpha = serviceAlpha;
            		tLayer.visibleLayers = serviceLayers;
            		myMap.addLayer(tLayer);	
            	}          	
            }
 
            private function configFault(event:FaultEvent):void
            {
            	Alert.show(event.fault.faultString);
            }

The XML is below: Note the configuration options that you would normally have to set in your compiled code. I just included the service name, url, alpha and visibility in this demonstration. There are many more options to choose from…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
< ?xml version="1.0"?>
<configuration>
<service>
	<name>
	Service Name
	</name>
	<url>
	http://yoururl.com/yourserver/rest/services/BaseMap/MapServer/
	</url>
	<alpha>
	1.0
	</alpha>
	<visible>
	true
	</visible>
	<layers>
	0,1
	</layers>
</service>
</configuration>

The next thing to take a look at is the fact that there is an Event that grabs the Latitude and Longitude from your map when it's clicked on (MouseEvent.CLICK). Moreover, it passes them to an HTML Input text box in the host page through an ExternalInterface call. The corresponding javascript should resemble the following. Note the < %= this.latitude.ClientID %> and < %= this.longitude.ClientID %> as they are required if you are embedding this in a ASP.NET for C# application.

?View Code JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script language="JavaScript" type="text/javascript">
//call flex function changeExtent
function mapextentchange(value)
{
	if(value != ''){
		getFlexApp('FlexMaps').changeExtent(value);
	}	
}
 
//called from flex
function displayExtent(latitude,longitude)
{
	document.getElementById("< %= this.latitude.ClientID %>").value = latitude;
	document.getElementById("< %= this.longitude.ClientID %>").value = longitude;
}
</script>

Finally, I snagged an example off of the ESRI Flex Community page that allowed me to toggle between Streetmap, Topographic Maps and Imagery data. As the selected Index changes, the base map changes...

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        <esri :ArcGISTiledMapServiceLayer
            visible="{bb.selectedIndex == 0}"
            show="layerShowHandler(event)"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer" 
            creationComplete="onBasemapCreationComplete()" />
        <esri :ArcGISTiledMapServiceLayer
            visible="{bb.selectedIndex == 1}"
            show="layerShowHandler(event)"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer" 
            creationComplete="onBasemapCreationComplete()"/>
        <esri :ArcGISTiledMapServiceLayer
            visible="{bb.selectedIndex == 2}"
            show="layerShowHandler(event)"
            url="http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer" 
            creationComplete="onBasemapCreationComplete()" />
 
    <mx :ToggleButtonBar id="bb" horizontalGap="2" selectedIndex="0" themeColor="0x00FF00" right="5" top="5">
        </mx><mx :dataProvider>
            </mx><mx :Array>
                </mx><mx :String>Streets</mx>
                <mx :String>Topo</mx>
                <mx :String>Imagery</mx>

So, I've had fun playing with this an I've actually been able to use the entire script in a project I was a part of. I hope that someone else gets as much use out of it as I did.