In this chapter, you have already learned how to create drilldown effects for map shapes and data points. At this point you should be able to make any shape or data point drilldown to another web page with relative ease. However, it may not yet be clear how you should go about creating a multiple level map drilldown system.
You may, for example, be wondering if you'll have to create a separate web page and/or appearance file for every map to which you wish to drilldown (which would be a very tedious task if, for instance, you wanted to be able to drilldown to every zip code in the United States).
Thankfully, PopChart+OptiMap provides a number of shortcuts. Through the use of dynamic objects, macros, and PCXML, you can create a multi-level, complex map drilldown system with just one web page and one appearance file.
About this Tutorial
In this section, we will show you how to build a basic drilldown system. Our goal will be to build a system that drills-down from the United States map to a county map for each state. Although this tutorial is limited to a two-level, US state-based implementation, the strategies and skills applied will aid in the creation of any map drilldown system, no matter how big the scope.
To keep things simple, we will not connect to any data (although at the end we will briefly discuss the implications of connecting to data). Also, we will provide example code in this tutorial for ASP only. However, with minor syntax changes you should be able to follow along in any PopChart+OptiMap-supported web scripting language.
This example assumes that you are running your web server, your PopChart+OptiMap server, and PopChart+OptiMap Builder on the same machine. If this is not the case, you will need to make adjustments accordingly (like uploading your PCXML file and/or ASP to the appropriate server and changing the server address in your ASP).
For your convenience the example appearance file produced in this tutorial is located at chart_root/examples/maps/drilldownUS.pcxml. The ASP produced in this example can be found at chart_root/examples/code/ASP/drilldownUS.asp. Translations of this web page into the other Corda-supported web application environments can be found under the respective directory for each environment in the chart_root/examples directory.
Note: Note the addition of a drilldown variable in the translations. This variable can be
eliminated if the proper drilldown destination has been set in the appearance
file in step 15.
Prerequisites
Before starting this tutorial, you should make sure you can publish a basic map to your web application server, as described in Chapter 3, "Using Corda Embedder."
You will also need to download and install the U.S. Counties map package from the Corda Technologies website (http://www.corda.com/download/maps/). These maps are free for licensed PopChart+OptiMap users.
Finally, you may want to read up on dynamic objects (see "Dynamic Objects") and drilldown (refer to the beginning of this chapter). We will try to explain these concepts throughout the tutorial, but it will help to already be familiar with them.
Tutorial
In this tutorial, we will create a two-level map drilldown system for U.S. states using one appearance file and one web page. This tutorial will take approximately 30 minutes.
To create a basic map drilldown system
- Start PopChart+OptiMap Builder and create a new appearance file.
- Add a dynamic object.
- Add other objects as needed.
- Test the appearance file using Preview With PCScript.
- Save your appearance file (File > Save).
- Save the ASP Sample Code for your appearance file to your web server.
- Open the page you just saved in a text editor, such as Notepad.
- Insert PCXML into the page to load a map over your dynamic object.
- Save your ASP, and test it to make sure it loads the map of the United States.
- Get your ASP ready for dynamically loading maps by defining variables to represent the map location and map name.
- Rewrite the map source PCXML string to make use of the map location and name variables.
- Insert an
if-thenclause that will override the map name and location based on information from the web page's query string. - Save your ASP, and test it by requesting a state map
- Return to the appearance file in PopChart+OptiMap Builder, and access the dynamic object's Properties dialog.
- In the Extra PCXML box, enter your global drilldown effect.
- Save your appearance file.
- Reload your main web page (the address you entered in step 9). When you click on any state, you should link to a detailed map of that state.
You can add a dynamic object by clicking on the Dynamic Object button on the toolbar, or by selecting Edit > Create Dynamic Object. This object is a placeholder object. We can replace it dynamically with a map using PCScript or PCXML.
You can add any other objects that you want, including text boxes and shapes. Note that to add a legend, you should make sure you have selected the dynamic object.
Also, you may want to resize the dynamic object. The size of the maps you eventually publish will be the same size as the dynamic object in the appearance file.
This step is mostly just to illustrate how the dynamic object works. Click on the PCScript tab at the bottom left corner of PopChart+OptiMap Builder. Then enter the following PCScript into the main window:
dynamic1.setPCXMLAttributes(FileName='maps\NorthAmerica\US\US.pcxml' ComponentName='US')
This PCScript tells PopChart+OptiMap the location (maps\NorthAmerica\US\US.pcxml, relative to the chart_root directory) and name (US) of the map that should replace your dynamic object.
After you have entered this PCScript, click on the Preview with PCScript button in the bottom right corner of PopChart+OptiMap Builder. You should see a map of the United States.
Note: Don't worry if you don't see your legend. The legend will not appear until after you have specified data.
You can use any name to save the appearance file. We have chosen drilldownUS.pcxml.
You can do this by clicking on the Sample Code tab in the bottom left corner of PopChart+OptiMap Builder. Then select ASP (COM Embedder) from the Sample Code For pull-down menu (if you are using a different scripting technology, you should instead select the appropriate web page for your environment). Make any necessary changes to the server address and image type, then click on the Save Code to File. Enter your file name, and save it to your web server (e.g. to C:\InetPub\wwwroot).
This can be done in two statements. First of all,
define a variable to hold the PCXML string that you will send to PopChart+OptiMap. In the example below, we have defined
this string as mapsource. You should add this line of
code immediately before you declare the CordaEmbedder
object (i.e. before the line that begins set
myImage...).
mapsource = "<DynamicObject Name='dynamic1' FileName='maps\NorthAmerica\US\US.pcxml' ComponentName='US'/>"
Like our PCScript in step 4,
the PCXML string above specifies the location and name of the map we want to
insert into our dynamic object. Also note that it contains the object name of
the dynamic object that we are replacing (dynamic1).
For more about object names, see "What
is My Object Named?".
Now, we can send this string to PopChart+OptiMap server using the addPCXML(String) command, as shown in the preceding
line of code. You should insert this line somewhere before calling getEmbeddingHTML(). For example, you could insert it
after the line that begins myImage.imageType.
myImage.addPCXML mapsource
Important: Make sure that your ASP page does not
contain any PCScript definitions. The pcScript
variable should be set to an empty string ("").
Otherwise, it may interfere with our example.
For example, if you saved your ASP to C:\Inetpub\wwwroot\drilldownUS.asp, you could test your ASP by browsing to http://localhost/drilldownUS.asp.
These definitions should occur before the map source
PCXML definition in step 8.
For example, you could call the map location variable maplocation. It should be set to a default value of maps/NorthAmerica/US/, since this is the base directory
for all of the maps used in this tutorial.
maplocation = "maps/NorthAmerica/US/"
Likewise, you could call the map name variable mapname. It should be set to US, since this is the top level map in this tutorial.
mapname = "US"
For the FileName attribute,
you will need to combine the map location and map name, followed by .pcxml. For the ComponentName
attribute, just use the map name. The line below shows how we would modify the
mapsource variable from step 8,
using maplocation and mapname from step 10.
mapsource = "<DynamicObject Name='dynamic1' FileName='" & maplocation & mapname & ".pcxml' ComponentName='" & mapname & "'/>"
Once you have made these changes, you may want to save your ASP and test it again to make sure there are no error messages.
The query string is a string of text that can be passed
to the web page from a submission form, a link, or in our case, a drilldown
command. Our query string will contain two pieces of information: the nameclass, which tells us the current level of the
drilldown system (e.g. State); and the map, which tells us which map to show. We will create this
query string in later steps, but for now we will worry about retrieving this
information in our web page.
We can retrieve these pieces of information from the
query string using the Request.QuerySting() method.
For example, Request.QueryString("nameclass") will
return the nameclass (our current drilldown level).
Note: In other scripting languages, the methods for retrieving query string parameters can vary greatly. Please consult the documentation for your scripting language for more information.
For our drilldown system, there will be two levels of
maps. The top level is the default level (the U.S. map), so we don't need to
worry about an if-then clause for it. However, our second level is the State level. Therefore, we will need to create an
if-then clause that checks to see if nameclass is
State, and makes appropriate changes to the
map name and location.
The following four lines of code show you what should be contained in this if-then clause. You should place these lines of code after your default map name and map location definitions, but before your map source definition.
If (Request.QueryString("nameclass") = "State") Then
mapname = Request.QueryString("map")
maplocation = maplocation & "Counties/"
End If
In the first line of code, we simply check the query
string for the value of nameclass. If nameclass does not exist, or is not equal to State, the remaining lines will be ignored. In the
second line, we reassign our map name to the value of the map parameter in our query string. In the third line, we
reassign our map location to the directory that contains the state maps we
want to display, which in this case is the Counties
folder inside of our base map directory (i.e. maps/NorthAmerica/US/Counties/). The last line closes the
if-then clause.
Just to make sure you're all caught up, here is the complete code for the ASP we have been building:
Example 8.1 ASP Code for a Map Drilldown System
<html>
<head>
<title>ASP Example</title>
</head>
<body>
<h1>Your image will appear below</h1>
<!-- Begin Embedder Code -->
<%
Dim pcScript
Dim mapname, maplocation, mapsource
maplocation = "maps\NorthAmerica\US\"
mapname = "US"
If (Request.QueryString("nameclass") = "State") Then
maplocation = maplocation & "Counties\"
mapname = Request.QueryString("map")
End If
mapsource = "<DynamicObject Name='dynamic1' FileName='" & maplocation & mapname & ".pcxml' ComponentName='" & mapname & "'/>"
set myImage = Server.CreateObject("Corda.Embedder")
myImage.externalServerAddress = "http://localhost:2001"
myImage.internalCommPortAddress = "http://localhost:2002"
myImage.appearanceFile = "examples/maps/drilldownUS.pcxml"
myImage.userAgent = Request.ServerVariables("HTTP_USER_AGENT")
myImage.width = 540
myImage.height = 330
myImage.returnDescriptiveLink = true
myImage.language = "EN"
myImage.pcScript = pcScript
myImage.imageType = "FLASH"
myImage.addPCXML mapsource
%>
<%= myImage.getEmbeddingHTML%>
<!-- End Embedder Code -->
</body>
</html>
To request a state map, we simply request the same web
page we requested in step 9,
except we add a query string at the end of the request. The query string
begins with a question mark (?). Then we list the
first parameter (the nameclass) and set it equal to
State (make sure you capitalize state). After
that, we enter an ampersand (&), followed by our
second parameter (map), which should be assigned to a
two digit state abbreviation (such as CA).
So, for example, if we were requesting a map of Texas, we would enter the following address in our web browser.
http://localhost/drilldown.asp?nameclass=State&map=TX
This can be done by double-clicking on the dynamic
object, or by selecting Edit > Properties.
To do this, we need to enter a <DefaultShapeSettings> PCXML element. This element
should contain a <Drilldown> tag, with a URL attribute set to the map's global drilldown string.
The global drilldown string is the most important part
of the equation. It tells each map shape (such as Texas or California) the web
page it should drilldown to. Our goal should be to create a drilldown string
that produces the same address as the one we entered in step 13.
However, we want this string to be universal, so that we don't have to enter
it for each state. We can make this string universal if we substitute the
%_NAMECLASS and %_NAME for
the name class and map name.
The line of PCXML below shows exactly what you should enter in the Extra PCXML box, assuming your web page is called drilldownUS.asp.
<DefaultBackgroundShapeSettings><Drilldown URL='drilldownUS.asp?nameclass=%_NAMECLASS&map=%_NAME'/></DefaultBackgroundShapeSettings>
Note that the macros in the URL string will be replaced with real values in our map on a state-by-state basis, producing drilldown destinations like the address we used in step 13.
Congratulations. You have successfully created a two-level map drilldown system.
Why this Worked: Conceptual Review
At this point, it is important to make several observations about why this strategy worked.
First of all, with the exception of top level maps (e.g. the world), every PopChart+OptiMap map file is named after the map object it contains. This is why we can specify the map file in step 11 by combining the map location with the map name. So, for example, the Texas map is named TX.pcxml because its main map object is called TX.
Secondly, PopChart+OptiMap map files are organized in such a manner that all of the maps on a certain level will always be contained in the same directory. Thus, in step 12, we can set the map location for any state map to maps/NorthAmerica/US/Counties/, since all the county-based state maps are in that folder.
Thirdly, you probably already realize that the dynamic object in our appearance file can be translated into any map object on the fly. However, defining the Extra PCXML in step 15 may still be somewhat of a mystery. Each dynamic object can store a number of PCXML elements inside of it. This PCXML means nothing to the dynamic object. However, when the dynamic object is transformed into a map, this extra PCXML overrides the default PCXML in the map. Thus, we are able to specify a global drilldown effect in our dynamic object by overriding the default drilldown effects in the map template. We could further extend this strategy to define default ranges for our map system, or create global data labels (see "Using a Dynamic Object as a Style Sheet").
The final pieces of the puzzle are the %_NAME and %_NAMECLASS macros, which
we used in step 15.
Each shape and/or data point in a map contains a number of attributes that are
used to identify it. Two of those attributes are Name
and NameClass, which can be retrieved in drilldown and
PopUp effects using the afore-mentioned macros.
Name, of course, represents the name of
the map shape or data point. In most cases, this name is exactly the same as a
map object (and therefore map file) containing a more detailed view of the
specified map shape or data point. For example, the US map contains 50 map
shapes, each of which is named after the two-digit abbreviation for the state it
represents (e.g. the Texas shape is named TX
which corresponds to the TX map file named TX.pcxml). Using the %_NAME macro,
we can retrieve the name of any state shape in the US map, which gives us the
ability to "know" the drilldown destination of any state just by using one
drilldown string.
NameClass, meanwhile, helps to define
the current level of a map drilldown system. As we have already seen, one of
these levels is State. Other potential values for NameClass include Country,
County, City,
Zip, and so on. Usually, all of the maps for a
particular name class can be found in the same folder.
To find out the name or name class of a map shape or data point
- Open your map in PopChart+OptiMap Builder.
- Select the shape or data point for which you wish to learn the code or long name.
- Select Properties > Override Background Shape Properties.
- The name and name class will appear in this dialog. You should also see other internal properties, such as the item's code, parent, and long name.
Adding Multiple Levels
You can add another drilldown level to your map system simply by
adding another if-then clause. The clause would look
very similar to the code we used in step 12
of the tutorial, only you would be checking for a different name class, and you
would need to adjust the map location directory accordingly.
For example, you've probably already noticed that the counties in our state map are drilldown-enabled (this is because our global drilldown string in step 15 applies to all maps, not just the U.S. map). However, when you click on the county, the map returns to the top level. This is because we haven't created an if-then clause to handle maps with a County name class.
If we wanted each county to drilldown to an outline map for that county, we could simply add the following if-then clause:
If (Request.QueryString("nameclass") = "County") Then
maplocation = maplocation & "Counties_Individual/"
mapname = Request.QueryString("map")
End If
Coincidentally, if you wanted to get rid of drilldown on the
second level (so that the counties don't drilldown at all), you could create a
drilldown variable that you would pass as an argument
to a myImage.addPCXML call (right after myImage.addPCXML mapsource). When you reach the bottom level
of your system, you could set this variable to disable drilldown. Otherwise, you
could leave it blank. The following PCXML disables drilldown:
<Map Name='dynamic1'><DefaultBackgroundShapeSettings> <Drilldown URL=''/></DefaultBackgroundShapeSettings></Map>
Connecting to Data
At this point, the only thing left to do is connect to data. Unfortunately, we don't have a sample database to connect this example to. However, we can at least review the strategy for connecting to data.
Strategy
Let's look back to step 12 of the tutorial. In this step, we retrieved several parameters from the query string. These parameters told us which map we needed to load. But we could also use these parameters to learn what query we need to perform on our database.
Let's say, for example, that the following query gave us our data for the state of Texas:
SELECT County,sum(Sales-Expenditures) FROM Revenue WHERE Date between #7/1/2003# and #7/31/2003# AND State = 'TX' GROUP BY County
We could make this query dynamic using the map name variable. Thus, in our if-then clause for the State name class, we could assign a variable named query to the following value.
query = "SELECT County,sum(Sales-Expenditures) FROM Revenue WHERE Date between #7/1/2003# and #7/31/2003# AND State = '" & mapname & "' GROUP BY County"
We could then send this query to PopChart+OptiMap later in our code using the Corda Embedder setMapDBQuery
method, as in following statement:
myImage.setMapDBQuery mapname, "areas", "", "Revenue", "", "", query
Accessing Secondary Information for Map Items
What if the information passed in the query string is not enough to determine the correct query? For example, what if you are on the county level, but in order to make the query you also need to know what state you are in. Or what if your database uses complete state names instead of two-digit abbreviations. Or what if you need to know the FIPS code for a state or a city?
Fortunately, each map item has a number of extra properties that
define secondary information for that item. We introduced two of these
properties LongName and Code
in "Map
Shape / Data Point Names". These properties increase the flexibility of PopChart+OptiMap's data import by allowing us to
reference data in tables and result sets using FIPS codes or a long names
instead of PopChart+OptiMap's default names.
But we can also use these properties in drilldown statements to
pass information from level to level. We can access these properties through the
%_LONGNAME and %_CODE macros
in drilldown effects (as well as in PopUp text and labels). These macros work
just like the %_NAME and %_NAMECLASS macros we used in step 15.
For example, if we needed to know complete state names (as
opposed to abbreviations) for our query statement, we could pass the LongName property as another parameter in our drilldown
statement:
<DefaultBackgroundShapeSettings><Drilldown URL='drilldownUS.asp?nameclass=%_NAMECLASS&map=%_NAME&longname=%_LONGNAME'/></DefaultBackgroundShapeSettings>
Then, we could access the state name by looking up the longname parameter in our query string, just as we looked up
the map and nameclass
parameters in step 12.
For a complete list of available properties and macros, see "Drill-down Macros".
Complete Example
Example 8.2 below shows how our drilldownUS.asp example might look connected to sample data.
Example 8.2 Map Drilldown System with Data Connectivity
<html>
<head>
<title>ASP Example</title>
</head>
<body>
<h1>Your image will appear below</h1>
<!-- Begin Embedder Code -->
<%
Dim pcScript
Dim mapname, maplocation, mapsource, drilldown, query
maplocation = "maps\NorthAmerica\US\"
mapname = "US"
drilldown = ""
query = "SELECT State,sum(Sales-Expenditures) FROM Revenue WHERE Date between #7/1/2003# and #7/31/2003# GROUP BY State"
If (Request.QueryString("nameclass") = "State") Then
maplocation = maplocation & "Counties\"
mapname = Request.QueryString("map")
query = "SELECT County,sum(Sales-Expenditures) FROM Revenue WHERE Date between #7/1/2003# and #7/31/2003# AND State = '" & mapname & "' GROUP BY County"
End If
If (Request.QueryString("nameclass") = "County") Then
maplocation = maplocation & "Counties_Individual/"
mapname = Request.QueryString("map")
query = "SELECT County,sum(Sales-Expenditures) FROM Revenue WHERE Date between #7/1/2003# and #7/31/2003# AND County = '" & Request.QueryString("longname") & "' GROUP BY County"
drilldown = "<Map Name='dynamic1'><DefaultBackgroundShapeSettings><Drilldown URL=''/></DefaultBackgroundShapeSettings></Map>"
End If
mapsource = "<DynamicObject Name='dynamic1' FileName='" & maplocation & mapname & ".pcxml' ComponentName='" & mapname & "'/>"
set myImage = Server.CreateObject("Corda.Embedder")
myImage.externalServerAddress = "http://localhost:2001"
myImage.internalCommPortAddress = "http://localhost:2002"
myImage.appearanceFile = "examples/maps/drilldownUS.pcxml"
myImage.userAgent = Request.ServerVariables("HTTP_USER_AGENT")
myImage.width = 540
myImage.height = 330
myImage.returnDescriptiveLink = true
myImage.language = "EN"
myImage.pcScript = pcScript
myImage.imageType = "FLASH"
myImage.addPCXML mapsource
myImage.addPCXML drilldown
myImage.setMapDBQuery mapname, "areas", "", "Revenue", "", "", query
%>
<%= myImage.getEmbeddingHTML%>
<!-- End Embedder Code -->
</body>
</html>
In this example, note that we have to add another parameter to our query string for the long name. To accommodate this additional parameter, we had to change our Extra PCXML from step 15 as follows:
<DefaultBackgroundShapeSettings><Drilldown URL='drilldownUS.asp?nameclass=%_NAMECLASS&map=%_NAME&longname=%_LONGNAME'/></DefaultBackgroundShapeSettings>
This addition was necessary since the county data in our
hypothetical database references counties by the county name instead of the FIPS
code, whereas the PopChart+OptiMap map templates store
counties under FIPS codes. Thus, for the county level, we need to use the FIPS
code when specifying the map and file location, but we need to use the actual
county name in the where clause of the query.
Fortunately, PopChart+OptiMap will accept both the county name (i.e long name) and FIPS code when importing data, meaning the tables produced by our query will import properly no matter how the counties have been indexed (see "Map Shape / Data Point Names").