Monday, September 21, 2009

From ArcMap to Google Maps in 10 Easy Steps


At NCTCOG, we use ESRI's ArcGIS Desktop product for most of our GIS needs (version 9.3.1 at the time of this writing). More and more, however, we get asked to mashup various spatial datasets into a web-friendly form using mapping APIs such as Bing or Google Maps. In my last post, I discussed the development of a web map using Google Maps and ArcGIS Server that required a decent amount of custom coding and heavy lifting. For many tasks, however, there is no reason to make things this complicated, especially if you have a small number of points.

A recent item I was tasked with was to create a web map from a spreadsheet containing sustainable development case-study sites. Users needed to be able to click on a point to get basic information about the site and a link to a more detailed PDF. There were less than 50 points, so I decided to generate a static KML file to get the job done. Kevin Martin from the City of Portland Bureau of Planning has kindly created and posted an "Export to KML" ArcMap extension that we'll use to do this.

Note that if your source data is already in an ESRI spatial format such as shapefile or geodatabase feature class, you can skip steps 2 - 4 (but obviously you'll still need to open ArcMap and add your shapefile).

1) Download and install the Export to KML ArcMap extension (you'll need admin rights for the install)

2) Open a blank ArcMap document and set the appropriate projection for the coordinates you are working with. A shortcut to doing this is to load a shapefile or feature class that is already in the correct projection. In this example, the spreadsheet contained coordinates in State Plane (FIPS 4202 for North Central Texas). If your coordinates are already in latitude/longitude (decimal degrees), the coordinate system you should select is at Data Frame Properties-->Coordinate System Tab-->Select a coordinate system-->Predefined-->Geographic Coordinate Systems-->World-->WGS 1984.

3) Load the spreadsheet in ArcMap. Right-click on the newly added spreadsheet layer (in the Source tab of the Table of Contents), and select 'Display XY Data'. If they were not automatically chosen, select the appropriate spreadsheet columns to use for the X and Y fields and click 'OK' (do not set the coordinate system).

4) Export the new XY events layer that was just created to a shapefile (use the coordinate system of the data frame) and add it to the map.

 


5) Click the Export to KML icon.  If you don't see it, right-click on any ArcMap menu and select 'Export to KML' from the menu list. If you don't have this option, it is not installed correctly. Select the new shapefile from the last step as your layer to export. There are a lot of options available in this tool that you can play around with, but here's what I used: Under Options-->Export Options tab: check the box that says 'Create Google Maps compatible descriptions'. Under Options-->Labeling and Description tab: Select an attribute for naming each feature (this will be the first line in the Google Maps info window) and add some optional HTML in the 'Feature description expression' field to design your info window contents. I used this:
 

<font size="-1"><br>
<strong>Location:</strong> [Location]<br>
<strong>Uses:</strong> [Uses]<br>
<strong>Notes:</strong> [Notes]<br>
<strong>Typology:</strong> [Typology]<br><br>
<a href="docs/[ID].pdf">More Information</a>
</font>


Notice the 'More Information' link. For this to work, PDFs must be added to a 'docs' folder in the web directory with the naming convention of 'x.pdf', where x is the unique ID of the site.

6) Save your KML file to a web-accessible location. Note: Our server is not configured for KML MIME-types, so I named the extension to '.xml' rather than '.kml', which can still be added using the GGeoXml object below. Depending on your server configuration, you may have to do the same.

7) Paste the following code inside the head tag of your web page. Note that you will need to sign up for your own Google Maps API key and replace the one you see below. You'll also need to change the location of the kml file reference as noted below:

 

<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAcRhm9JfOYaiJEDdKsdN5iRRLoqfmfP4Tw-36gNTGEjCz74fmTRRhHLDWR4bjMeaQnhaJs3L_Qd_BjA" type="text/javascript"></script>
<script type="text/javascript">
    var gmap = null;
    var kmlLayer = null;
   
    function initialize() {

        gmap = new GMap2(document.getElementById("gmap"));
        var centerat = new GLatLng(32.9, -97);  //change the center coordinates here

        gmap.addControl(new GLargeMapControl());
        gmap.addControl(new GMapTypeControl());
        gmap.addMapType(G_PHYSICAL_MAP); //optional:  add the nice Google terrain basemap
        gmap.setMapType(G_PHYSICAL_MAP); //optional:  set the terrain basemap to display as default
        gmap.enableScrollWheelZoom();
        gmap.setCenter(centerat, 9);


        kmlLayer = new GGeoXml("http://www.visionnorthtexas.org/NTAF/casestudies/studymap.xml"); //change reference to your kml file location here
        gmap.addOverlay(kmlLayer);
    }
    
</script>


8) in the body tag, add this: onLoad="initialize()" so it will look something like this:
 

<body onLoad="initialize()">



9) Add this wherever you want in the body section of your web page:


<div id="gmap" style="width: 700px; height:600px;"></div>

You can change the width and height values as needed.

10) If you want to use a custom icon, change the 'href' value in the style tag of your kml file to reference the png icon of your choice, as seen below. Note that Google Maps caches your kml, so if you do not see the change reflected when you reload the page, you can trick it into updating by renaming the kml file and updating the reference to it in your web page code.


<Style id="FEATURES">
    <IconStyle>
      <color>FF009C00</color>
      <scale>0.333333333333333</scale>
      <Icon>
        <href>http://www.visionnorthtexas.org/NTAF/casestudies/redstar.png</href>
      </Icon>
    </IconStyle>
    <LabelStyle>
      <color>00FFFFFF</color>
    </LabelStyle>
</Style>  

That's it. See the result here.

Friday, September 4, 2009

Mapping landfills with ArcGIS Server and Google Maps

I just put the finishing touches on a web map of municipal solid waste facilities and closed landfills and I thought it made a good candidate for my first ever blog post. The Environment and Development (E&D) department here at NCTCOG wanted to give their previous application a facelift. The old way of doing things was to have a static map image for each landfill, but now we can take advantage of ArcGIS Server and web mapping APIs such as Google Maps to enhance the user experience.

We have done several projects using ArcGIS Server with Microsoft's Bing Maps API (formerly Virtual Earth), but I chose Google Maps for this project for several reasons: 1) I wanted to see what it was like to develop with ESRI's ArcGIS Javascript Extension for Google Maps; 2) ESRI's Google Maps extender supports the display of dynamic ArcGIS services (Bing doesn't); and 3) I saw a post by Dave Bouwman that talked about displaying large datasets in Google Maps using the ESRI REST API which gave me a good start.

Before I talk about the front-end, let me provide some insight into the data that is driving it. These datasets fall into an area that historically has been tricky to deal with in that non-GIS staff are responsible for editing attributes for each landfill or facility while GIS staff are responsible for editing the corresponding points and polygons. So what I ended up doing is migrating old point and polygon shapefiles to SDE feature classes and migrating an old Access da
tabase with the non-spatial attributes to SQL Server. The feature classes essentially only contain one attribute value: the unique ID of each facility. In ArcMap, I can use this unique ID to join the SQL Server table to the SDE feature classes so we don't have to query multiple data sources to provide both spatial and tabular information to the end users. (Note that if we were dealing with a huge number of features this ArcMap join method would have serious performance implications but since we have less than a thousand, it works great.)

With my data symbolized and joined in ArcMap and saved into a .mxd file, I then created an ArcGIS Server dynamic map service that can be easily draped over the Google base map. I chose to keep the service dynamic because the database is likely to change often (at least in its early stages) and I didn't want to have to re-cache every time a change occurred. I also needed the ability to dynamically turn features on and off based on certain criteria without having to create a separate service for each.

E&D wanted to have the ability to separate the Closed Landfill
Inventory from the active landfills and solid waste facilities without losing the ability to view them together. To do this without creating a separate application for each, I allow the page to be called with different URLs (querystrings) that will cause the application to present 3 different modes: 1) active landfills and solid waste facilities, 2) Closed Landfill Inventory, and 3) all of the above. In javascript you can parse out querystring values from the location.search property. Depending on the mode, the javascript will change the default map view using the setLayerDefinitions() ESRI function for dynamic layers and ASP will render the appropriate form checkboxes used to toggle map features. You can check out the different modes using the 'Change Map Type' drop-down above the map.

Due to the large potential number of points, rather than try to render each as an object (aka marker in the Google lingo) I chose to display the features in their PNG-tile form. The user can click on a point and which runs an ESRI identify task and presents the result in an info-window with the results. The nice thing about this is that the user can click in an area with a lot of overlapping (at least in appearance) points and be presented with a table list of all of the points that fall within a specified tolerance. The downside of not using markers is that the user does not get the nice little cursor change when they hover over a point that hints to the fact that they can click.

You can view the javascript code driving this app here. A few more noteworthy features of this application:

Zoom to polygon using ESRI FindTask. Selecting a county from the drop-down queries uses the esri.arcgis.gmaps.FindTask method to return the matching county. We can then use .getBounds(), .getCenter(), and .getBoundsZoomLevel() methods to get the appropriate values to feed into the .setCenter map method.

Use of cookies to save previous map extent and type. Originally, when users would view the details page for each site and then return to the main page, the map would reset to the default coordinates hard-coded into the initialize() function. To force the map to save its previous state, temporary cookies are used to save the map center point, zoom level, and map type (satellite, terrain, hybrid, etc).

Limiting displayed identify results to match the current layer definition. Unfortunately ESRI's identify task does not respect the current layer definition, so even if the user had toggled everything off with the exception of Active Landfills, the identify results that would populate the infowindow would include any features that fell within a certain tolerance of the user's mouse click. To solve this, I create the isIdentifyResultInCurrentMapFilter function to determine whether or not to display the results as we loop through each. This is a hack and I'm willing to bet there is a better approach out there (let me know if you have one).

Thursday, September 3, 2009

WELCOME!!


Thank you for visiting our new GIS blog. This is the brainchild of the internal GIS group at NCTCOG (North Central Texas Council of Governments). Due to our varying specialties & interests, we hope to bring a new, diverse voice to the GIS blogosphere. We are just getting started - but should have some goodies up soon.