main page - page initiale - hauptseite - página principal projects - projets - projekte - proyectos authors - auteurs - autoren - autores papers - exposés - berichte - papeles help - aide - hilfe - ayuda

carto:papers:svg:examples:interactivity and svg gui:navigationTools

Navigation Tools for SVG Maps, Version 1.3.1 (2007-05-07)

Example provided by Juliana Williams and Andreas Neumann (see history at the end of the page)

This SVG file structure and related ECMAScripts should assist authors of "SVG only" webmapping applications in creating 2D map navigation tools. For an introduction on why you should at the current time consider writing SVG only webmapping applications, see the selectionList tutorial.

See an example of the SVG navigation tools. The scripts have been tested in Adobe SVG viewer version 3, version 6, Mozilla SVG, Opera 9 and Batik. They have been tested in multiple browsers and operating systems (Linux, MacOSX, Windows).

The SVG File and ECMAScripts are free to use. If you do any substantial improvements, please send back your improvements to the author. The GUI elements of carto.net are licensed under the terms of the LGPL license and you need to distribute the license with your code. Additionally, you should create a link to carto.net in your project's "about page" or impressum. If you plan to use the GUI elements in a commercial product, please check back with the author of the GUI elements to investigate arrangements. The original URL for the navigation tools is http://www.carto.net/papers/svg/navigationTools/.

Documentation

Features

Dependencies on external files and functions

  1. navigation.js:
    • all of the objects and functions in this file are necessary
  2. mapApp.js:
    • all of the objects and functions in this file are necessary
  3. helper_functions.js:
    • all the global variables at the top of the file
    • function toPolarDir()
    • function toPolarDist()
    • function toRectX()
    • function toRectY()
    • function leftOfTest()
    • function intersect2lines()
    • function formatNumberString()
    • function statusChange()
    • function scaleObject()
    • function getTransformToRootElement()
  4. timer.js:
    • all of the objects and functions in this file are necessary
  5. slider.js:
    • all of the objects and functions in this file are necessary
  6. button.js:
    • all of the objects and functions in this file are necessary
  7. checkbox_and_radiobutton.js:
    • all of the objects and functions in this file are necessary

Additional Requirements

Note that any map data that you use in this environment has to be y-axis inverted. In typical GIS situations higher y(or up)-values are farer to the north (top) whereas in SVG its the opposite. Changing your data is relatively easy: use the original coordinate system and multiply each up-value with minus 1. Our Shapefile to SVG converter does this inversion for you, so does Postgis. It is not recommended to flip the entire map using a transform attribute, since you would also flip text labels.

Step by Step Instructions

Step 1: Copy of the initial file structure

Copy all of the above .js files to your local disc. Additionally, copy the files index.svg, relief.jpg and relief_small.jpg (data source: Yosemite National Park website) to your local disc to get a working starter copy. You may later delete the .jpg files after you added your own data. Next, adjust the relative paths to the external javascript files in the script-tags (after the svg-root element) in the file index.svg. We will use the file 'index.svg' as a starter to fill in our own data. Please do not remove any elements containing id's in this file. Otherwise the scripts aren't guaranteed to work any more. Most Elements without an id-attribute are optional and may be removed. Now, open the file 'index.svg' and make sure the prototype works with the initial settings (Yosemite National Park).

Step 2: Adjust the coordinate systems

The file index.svg uses 3 different coordinate systems. The outermost coordinate system is device oriented (screen pixels) and should be adjusted to your needs. It is defined in the svg root element (line 4) in the viewBox attribute. (If you don't know the concept of the viewBox coordinate systems, please check with the SVG specification, chapter 7.7) In the same element we define the width and height to 100% and disable zooming and panning (zoomAndPan="disable"), since these functions will be replaced by our own functions. If you design for regular desktops, we recommend that you more or less keep the screen oriented coordinate system as we defined it in the svg root element. Please note, if you considerably change the screen oriented coordinate system, you will also need to adjust the size of the symbols later in the file to reflect your coordinate system changes.

Figure 1: Nested coordinate system principle

The second coordinate system is the coordinate system of the main map window. We define it within a nested <svg /> element. This coordinate system is defined in "real world" units, e.g. meters or degrees. In our example we defined the coordinate system in meters, reflecting UTM zone 11 coordinates. Note that in our viewBox coordinate system the y-axis is inverted, since map coordinate systems usually run opposite to the SVG coordinate system. For your own map, search for the line starting with <svg id="mainMap" ... and adjust the viewBox, width, height, x and y attributes to reflect your coordinate system and placement of the map within your screen coordinate system. Please note, that for the script to work correctly, we assume that the ratio of the outer width and height of the mainMap is the same as the width and height defined in the viewBox in real world coordinates.

The third coordinate system is internally the same as the main map window coordinate system, which means that the viewBox attribute of the referenceMap should be the same as in the mainMap. Search for the line starting with <svg id="referenceMap" ... and adjust the x, y, width, height and viewBox attributes to match your layout and map coordinate system.

Please note that the script is designed in a way that you need to first define the maximum viewBox. If you want to initally start the application with an already zoomed in viewBox, you should still fill in the full values in the initial SVG file and later call the mainMap.setNewViewBox() method as described below.

Step 3: Adding static geometry to the map and reference map

In order to verify that our tool works correctly, it is useful to add some simple base geometry (raster or vector) both to our main map and the reference map. The geometry within the reference map should be very basic and generalized and serves only as a basic orientation. It should be small in filesize and need not be very accurate. Within the main map, you may place single elements (e.g. image or path) or whole groups within the group with the id "mainMapGroup". Please do not place them outside of this group or the panning tool will not work. You should bear in mind, that our coordinate system inverts the y-axis. If you convert from GIS data you should add a minus sign in front of the y-coordinates. Our Arcview Shapefile to SVG converter does this for you. Alternatively, to quickly see any results, you could use a georeferenced raster image (jpeg or png) as it can be exported from any GIS system. You may consult the existing geometry in our Yosemite map as a reference. In order to toggle layer visibility, it is necessary to set a unique id to the group or elements within the main maps. (see step 6)

Step 4: Adjusting the init function in the javascript

Next we'll have a look into the script section of the file index.svg. The two global variable definitions as listed here need to stay there, otherwise the script is not guaranteed to work. Within the init function, the two lines var dynamicLayers = new Array(); and var digiLayers = new Array(); need to stay there. These are currently empty arrays that can later be used, if you want to add dynamic layers (loaded from a database) or editable layers for digitizing. Currently, we'll leave them as empty arrays, that are handed over to the map object (mainMap) defined in the next line. The map object holds various data on the map and also enables the zooming and panning tools. As parameters we send the following data:

Constructor of map object in order of the parameters to be passed over:

Example:

//define some styles for the map object
var zoomRectStyles = {"fill":"none","stroke":"crimson","stroke-width":0.002,"stroke-dasharray":"0.012,0.002"};
var highlightStyles = {"stroke":"crimson","stroke-width":0.002};
var dragRectStyles = {"fill":"lightskyblue","fill-opacity":0.5};
myMainMap = new map("mainMap",64000,640,0.6,0,26911,"m",1,true,"coordX","coordY",dynamicLayers,digiLayers,"",zoomRectStyles,highlightStyles,dragRectStyles,"referenceMap","myDragCrossSymbol",4750);

In the next line we'll invoke the zoomSlider. We add the slider object instance as a property of the myMapApp object instance to avoid more global variables. We'll hand over the following parameters:

Constructor of slider object in order of the parameters to be passed over:

Example:

myMapApp.zoomSlider = new slider(710,70,myMainMap.minWidth,710,170,myMainMap.maxWidth,myMainMap.maxWidth,
"mapZoomSlider","dimgray",2,10,"sliderSymbol","myRefMapDragger.resizeDragger",true);

Next you need to reposition or adapt your navigation tool buttons to fit in your map layout. All buttons make use of the button object. They are created per script. Some of the buttons are implemented as simple "button" objects, some of them are "switchbutton" objects. For details of the button properties and methods please see the following documentation. You can adapt the look and feel of the button and symbol instances using presentation attributes or CSS styles. All symbol definitions are implemented with <symbols /> (residing in the <defs /> section). Each button also needs an empty group in the DOM hierarchy where the new button geometry can be appended. This empty group needs a unique id which has to match the parameter one in the button constructor. In our example, look for the group after the XML comment line <!-- this group holds symbol instances for navigation tools -->. To reposition all buttons as a whole group you may add transform attributes on the parent group of the button groups (e.g. transform="translate(10 10)"). Please note that you should change the button settings with care. You should only adapt the position, width and styles of the button, and not the related function calls or ids, otherwise the navigation functions won't work. If you want to delete one button, it is better to set the display or visibility of that group to "none" or "invisible", otherwise you would have to modify the navigation scripts.

myMapApp.buttons = new Array();

This line creates a new array in our global "myMapApp" object instance to hold all references to the zoom and pan buttons.

Constructor of button object see button documentation.

Example:

//button styles, adopt the style settings to match your needs
var buttonTextStyles = {"font-family":"Arial,Helvetica","fill":"dimgray","font-size":10};
var buttonStyles = {"fill":"white"};
var shadeLightStyles = {"fill":"rgb(235,235,235)"};
var shadeDarkStyles = {"fill":"dimgray"};
//button instance creation
myMapApp.buttons["zoomIn"] = new button("zoomIn","zoomIn",zoomImageButtons,"rect",undefined,"magnifyerZoomIn",705,47,20,20,buttonTextStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,1);

Note that we use both button and switchbutton objects that have slightly different constructors and behaviour. Note the way how we create the Array of Literals defining the presentation attributes of the button look.

myMainMap.checkButtons();

After creating the buttons we call the .checkButtons() method of the myMainMap object instance. This method analyzes the current map extents and disables/enables buttons. In some situations buttons should not be active. As an example, if the map is in full view, the "zoomOut", "panManual" and "recenterMap" buttons are deactivated. This method is called after each change of the map extent.

The last line calls the function loadProjectSpecific(). This function is always called, after resetting the map extent, e.g. after zooming and panning. In this function you can react to the changed map extent. In our example we reset the numbers indicating width and height of the map extent and change the line-width of the national park boundary to a fraction of 0.005 of the current map extent. If you remove the park boundary from the mainMap layers, you should also remove the last line in this function, otherwise you will get error messages. If you dynamically load map layers from databases or webservices, you can use this function to update the map geometry. This is described in the tutorial Dynamic Loading of Vector Geodata for SVG Mapping Applications Using Postgis, PHP and getURL()/XMLHttpRequest().

You should now test the functionality of the zoom and pan tools again. If you receive error messages, or something behaves strange, please check back with the instructions above.

Step 5: Adjusting checkBoxes to toggle layer visibility

In a next step we want to toggle the visibility of our map layers. Within "index.svg" search for the section containing the checkboxes. You should find something similar to the following:

<!-- control map layer visibility with checkBoxes -->
<g transform="translate(590 225)" id="checkBoxes">
	<text font-family="Arial,Helvetica" fill="dimgray" font-size="18px" font-weight="bold" x="-10" y="0" pointer-events="none">Map Layer Controls</text>
	<!-- the checkbox geometries will be added here -->
</g>

This is the group where you should place the checkBoxes that will be created by script. Of course you should adjust the transform attribute of the group element. Next, have a look at the script section (function init) and look for the following lines:

//create checkbox array
myMapApp.checkBoxes = new Array();
//labeltext styles
var labeltextStyles = {"font-family":"Arial,Helvetica","fill":"dimgray","font-size":15};
//create individual checkboxes
myMapApp.checkBoxes["park_boundary"] = new checkBox("park_boundary","checkBoxes",0,20,"checkBoxRect","checkBoxCross",true,"Park Boundary",labeltextStyles,12,6,undefined,toggleMapLayer);
myMapApp.checkBoxes["ShadedRelief"] = new checkBox("ShadedRelief","checkBoxes",200,20,"checkBoxRect","checkBoxCross",true,"Shaded Relief",labeltextStyles,12,6,undefined,toggleMapLayer);

First, you create an array for checkBox elements in the global myMapApp object instance. Next you define the styles, similar to what we have discussed above, and finally you add checkBox instances following the instructions at the checkbox and radiobutton tutorial.

Step 6: Final layout and styling

In this last step you need to work on the styling of the map layout. Simply replace all the fill, stroke, opacity, font-attributes, etc. as you like. Next to these XML attributes you might want to adjust positions of layout elements and add map titles, imprint, data sources, etc. Note that it is easier to move elements in groups, e.g. <g transform="translate(50 100)"> For positioning elements and testing the functionality of the navigation tools it is recommended to use Apache Batik since it displays the viewBox coordinates in the status bar of the squiggle browser. Additionally, it offers usually more meaningful error messages than other browsers and it features a js script debugger.

Constructors

See in the section above (step 4).

Selected Useful Methods of the map Object and Other Functions

showExtent() is a standalone function that alerts the current map extent. In our example this function is attached to the tiny rectangle in the lower right corner of the mainMap. Clicking on that rectangle activates this function. This function is dependent on the existance of the "myMainMap" instance of the "map" object.

loadProjectSpecific() is a standalone function that is always called when the map extent changes. It can be used to adjust dependent map elements accordingly or restyle them in dependency of the current map width.

Useful Properties of the Map Object

Current Limitations

Version history




Last modified: Monday, 07-May-2007 14:47:02 CEST
© carto:net (andré m. winter & andreas neumann)
original URL for reference: http://www.carto.net/svg/navigationTools/index.shtml
del.icio.us Add this page to del.icio.us