Javascript Concepts

Google Maps API Tutorial

avascript Concepts: Pass by Value

I was recently asked whether Javascript function parameters are passed by Reference or by Value. The really quick answer is that they are passed by Value. The full answer is actually quite complicated, and I won’t be going into all the details.

What’s it all about

Some computer languages allow function parameters to be passed either by Reference or by Value.

When a parameter is passed by Reference, then any changes made to it inside the function also change the copy of the variable that’s held outside the function. The variable used inside the function is a pointer to the external copy of the variable

When a parameter is passed by Value, then the function has its own local copy of the variable. Changes to the local copy don’t affect anything outside the function. The variable used inside the function is a local variable that (initially) has the same value as the external variable.

Created by Reference

However, Javascript differs from some other languages in that complex variables are created by Reference.

When you create a numeric or Boolean variable in Javascript, then that variable contains the numeric value you specify.

When you create any other type of variable, such as a string or an Object(), then the variable contains a Reference to the chunk of memory where the data is located. In some computer languages you might say that Object() variables are pointers.

For rather technical reasons, you don’t need to worry about strings. They end up behaving just as if they were passed by Value.

The fact that Object() variables are references does affect what happens when a function modifies a property of a passed Object(). Changing or adding a property of an Object() does affect the property of the external copy of the Object().

For example:

 function tweakIcon(a) { a.image="mymarker.png"; a = some_other_icon; } var icon = new GIcon(); tweakIcon(icon); alert(icon.image);

The “icon.image” property has been changed to “mymarker.png”, but “icon” isn’t affected by the “a = some_other_icon;” statement.

Assignment by Reference

Javascript assignment statements for complex variables are by Reference.

For example:

 var marker1 = new GMarker(point); var marker2 = marker1;

creates a second variable that points to the same chunk of memory where the marker data is located. The two variables are references to the same Object()

Whereas this:

 var marker1 = new GMarker(point); var marker2 = marker1.copy();

creates a second marker that has the same properties.

Comparison by Reference

Javascript comparison operations are by Reference.

if (marker1 == marker2) is true if the variables reference the same marker Object(), but false if marker2 is a .copy() of marker1.

Advertisements

Javascript Concepts

Google Maps API Tutorial

Javascript Concepts: Function Closure

Function closure is a fairly unusual computer language concept so it may take you by surprise if you’re familiar with other programming languages.

What happens is that each time a function closes, a copy of the values of its local variables is retained. Technically, a copy of the function’s “activation frame” is retained, and that contains all the local variables.

The key feature of function closure is that any callback functions (such as event handlers) which were created when the function executed will have access to the preserved activation frame, and its local variables.

Activation Frame

Activation frames do occur in other languages that support recursive functions. Consider this code

    function factorial(a) {
      if (a == 1) {return 1}
      else {return a*factorial(a-1)}
    }

When you call “factorial(5)”, then five separate instances of the factorial() function get executed, each one has its own activation frame, which contains its own local variables. In particular, each instance of the function has its own value for “a”.

Memory

If a function closes without creating a callback function, then, in a properly written browser, its activation frame will eventually be destroyed by the Garbage Collection system, and the memory recovered.

The variables are only retained if something continues to hold accessible references to them.

Closure

Note that the value of the variable that is retained is the value at the time that the function closed, not the value at the time that the callback function was created.

Updating closure variables

A callback function can change the values of variables in the activation frame of the closed function. It can’t, however, create new variables in that activation frame.

   function f1() {
      var foo=123;
      GEvent.addListener(map,"click",function(overlay,point) {
        GLog.write(foo);
        foo++;
      });
      var foo=321;
    }

The first time the map is clicked, the value “321” will be displayed (the value of “foo” when the function closed).

The second time the map is clicked, the value “322” will be displayed. The value of “foo” in the activation frame has been updated by the previous click callback.

Javascript Concepts

Google Maps API Tutorial

Javascript Concepts: Scope

In common with most modern computer languages, Javascript variables and functions have “scope”. What this means is that the variable or function can only be accessed from within the function that “var”ed it.

In languages without scope, such as early versions of BASIC and COBOL, people working on different parts of the same program had to be careful not to use the same variable for different purposes. If Fred wrote a useful BASIC subroutine that used the variable “A”, and Dave incorporated that subroutine into one of his programs that also happened to use a variable called “A”, then calling the subroutine would change the value of “A” which might cause Dave’s program to behave incorrectly in a completely different part of the code that expected the original value.

In a scoped language, Fred can write a function that uses separate “local” variables which only exist inside that function, and there’s no danger of them clashing with “global” variables of the same name that exist in the calling program.

If you really really want to, you can explicitly access the global variable, by referring to it like “window.A”. Global variables are properties of the “window” object.

Undeclared variables

If you assign a value to a variable without first “var”ing it, then a global variable of that name is created for you.

In MSIE, there’s a conflict with the fact that it already creates a global variable that matches the “id” of each element that has an “id”. Attempting to assign a new value to a variable that MSIE created that way, without using “var”, will throw an error.

Not block structured

If you’re familiar with block-structured languages, such as C, Pascal and Java, then you might possibly be caught out by the fact that Javascript is not block-structured. Variables that are created inside blocks (such as for loops and if statements) persist until the end of the function. For example, if you’re used to a block-structured language, you might expect variable in this example to display the value “123”.

   function foo() {
     var i=123;
     for (var i=0; i<3; i++) {
       ...
     }
     alert(i);
   }

In Javascript, the value displayed is “3”. That’s because “i” is local to the function, not local to the “block”, so the “i” used inside the for is the same as the one declared outside the for loop.

Note that Javascript doesn’t complain if you “var” the same variable more than once within the same scope.

Calling Javascript from HTML

Whenever you make calls from HTML to Javascript, e.g. from the “onclick” of a button, the called Javascript executes in global context. This means that any functions or variables referenced in the “onclick” string must be global.

Local functions

In the same way that variables can be local to a function, functions can be local to another function.

It’s all to easy to do this by mistake if you change your code from something that runs inline to something that’s called from <body onload=”loadMap()”>. You can’t just wrap “function loadMap() {” … “}” around the whole of your code and expect it to work because functions that were previously global will have become local functions of loadMap().

Third Party Extensions

Google Maps API Tutorial

EGeoXml

The primary objective of the EGeoXml extension is to be able to take a “My Maps” KML file, change the file extension to .XML and render the markers, polylines and polygons under the API in a manner that exposes those objects, allowing them to be manipulated by the calling program.

A secondary objective is to support a reasonable range of types of KML files that were not created with MyMaps.

The extension is far from complete, but it does render markers, polylines, polygons, info windows and sidebar.

A more complete extension is available from Lance Dyas: http://www.dyasdesigns.com/geoxml/

How to use it

  1. take a copy of egeoxml.js, and place it in the same folder as your code.
  2. Call the extension code into your page after loading the API code, like this
         <script src="egeoxml.js" type="text/javascript"></script>
  3. Create an EGeoXml() instance, like this
         var exml;
         exml = new EGeoXml("exml", map, "test001.xml");
  4. Call the .parse() Method, like this
         exml.parse();

Obtaining the KML file with the Placemarks

If you click the “View in Google Earth” link of the My Maps page, then you get a KML file that contains a NetworkLink instead of a list of Placemarks. EGeoXml can’t do anything with that.

To obtain the real KML file from My Maps, you now have to right click on the “View in Google Earth” link, copy the link location (“copy shortcut” in MSIE) and then edit the resulting URL.
Find the bit that says “&output=nl” and change it to “&output=kml”.

Constructor

The EGeoXml constructor takes 4 parameters.

  1. A text string containing the name of a global variable into which you will be storing the returned reference.That’s a bit of a weird one, but I can’t see any way round it. I need a global variable so that I can create the HTML for a sidebar entry that can reference the EGeoXml properties (calls from HTML execute in global context) but I don’t think I can safely create global variables from inside a method, particularly if I want to allow you to use more than one EGeoXml instance in the same page.It might sound complicated, but you just have to make sure that when you write
    foo = new EGeoXml(“foo”,… the two “foo”s are the same.
  2. The map on which the objects are to be rendered
  3. Either A text string containing the URL of the XML file. It must be on the same server.
    Or An array of strings containing the URLs of such XML files.
  4. An optional set of {options}.

The currently available set of options are:

  • {icontype:”style”} Tries to use the icons specified in the “Style” tags of the XML file.
  • {icontype:”default”} Always uses the G_DEFAULT_ICON for the markers. If absent {icontype:”style} is used.
  • {sidebarid: … } A string containing the ID of a div to be used for the sidebar. If absent, no sidebar is generated.
  • {dropboxid: … } A string containing the ID of a div to be used for the drop down selection box. If absent, no dropbox is generated.
  • {sidebarfn:…} A reference to your own fundtion for creating sidebar entries. See below
  • {dropboxfn:…} A reference to your own fundtion for creating drop down selection entries. See below
  • {noshadow:true} Plots markers with no shadows. Doesn’t work with {icontype:”default”}.
  • {iwwidth: … } An integer specifying the width of the info window detail div. If absent, it will be set to 250.
  • {titlestyle: … } A string controlling the style of the info window title field, see below.
  • {descstyle: … } A string controlling the style of the info window description field, see below.
  • {directionstyle: … } A string controlling the style of the info window title field, see below.
  • {baseicon: … } A GIcon() to be used as a basis for the marker icons, see below.
  • {iwoptions:{ …}} A set of GInfoWindowOptions to be applied to the info windows. E.g. {iwoptions:{maxHeight:300,autoScroll:true}}
  • {markeroptions:{ … } A set of GMarkerOptions to be applied to the markers. E.g. {markeroptions:{draggable:true}}
    If you set the “icon” option, then the information from the XML file will be countermanded by your setting.
  • {createmarker: … } A reference to your own createMarker() function.
    Use this if you want to handle your own marker and sidebar creation.
    The function will be called with parameters containing the GLatLng() of the location, then strings containing the “name”, the “description” and the “styleUrl” fields from the XML file.
    NOTE: if you write your own createMarker function, then it’s up to you whether you implement any of the previous options.
  • {createpolyline: … } A reference to your own createPolyline() function.
    Use this if you want to handle your own polyline creation.
    The function will be called with the same parameters as new GPolyline(), i.e. points, colour, width, opacity.
  • {createpolygon: … } A reference to your own createPolygon() function.
    Use this if you want to handle your own polygon creation.
    The function will be called with the parameters: points, colour, width, opacity, fillcolour, fillopacity, bounds, name, description.
  • {addmarker. … } A reference to your (G)MarkerManager.addMarker() helper function. Use this if you want to manage your markers. See below.
  • {nozoom:true} If this option is absent, then the view will be zoomed to fit the data.
  • {sortbyname:true} The sidebar will be sorted by name.
  • {directions:true} Activate direction finding and local search in the info window
  • {printgif:true} Use gifs when printing icons.
  • {printgifpath:…} A string containing the location of the print gifs.
  • {elabelclass:…} A string containing the name of the CSS class to be used for ELabels, see below.
  • {elabelopacity:…} A number from 0 to 100 specifying the percent opacity of the ELabels.
  • {elabeloffset:…} A GSize specifying the offset of the ELabels.
  • {preloadimages:true} Attempts to preload images mentioned in the description, see below.
  • {polylineoptions:{ … } A set of GPolylineOptions to be applied to the polylines. E.g. {polylineoptions:{clickable:false}}
  • {polygonoptions:{ … } A set of GPolygonOptions to be applied to the polygons. E.g. {polygonoptions:{clickable:false}}

Properties

The internal structure of the EGeoXml instance is exposed via these properties:

  • .myvar – stores the first passed parameter from the Constructor.
  • .map – stores the second passed parameter from the Constructor.
  • .url – stores the third passed parameter from the Constructor.
  • .urls – stores an array of URLs to be processed.
  • .opts – stores the fourth passed parameter from the Constructor.
  • .iwwidth – a copy of the opts.iwwidth parameter, or 250 of that parametr is absent.
  • .bounds – a GLatLngBounds object which will be used for the zoom-to-bounds option.
  • .gmarkers – array of markers.
  • .gpolygons – array of polygons.
  • .gpolylines – array of polylines.
  • .groundoverlays – array of ground overlays.
  • .side_bar_html – the HTML string that is used to create the sidebar.
  • .side_bar_list – array used for sorting the sidebar.
  • .styles – an associative array containing information from the Style tags of the XML file.
  • .progress – the number of GDownloadURL requests currently in progress.
  • .lastmarker – a reference to the last marker that was clicked

Methods

EGeoXml.hide() Performs .hide() on all the markers, polylines, polygons and ground overlays, and hides the sidebar or dropbox.

EGeoXml.show() Performs .show() on all the markers, polylines, polygons and ground overlays, and unhides the sidebar or dropbox.

EGeoXml.parseString(txt) accepts a string of text containing the KML data an parses it.

It also accepts an array of strings, each string being a complete KML document.

You can use this instead of EGeoXml.parse().

When using .parseString the “url” parameter of new EGeoXml is ignored, so the whole thing looks like this:

     var exml;
     exml = new EGeoXml("exml", map, null, {sidebarid:"the_side_bar"});
     exml.parseString('<?xml version="1.0" encoding="UTF-8"?> ... </kml>');

or

     var exml;
     exml = new EGeoXml("exml", map, null, {sidebarid:"the_side_bar"});
     var doc1 = '<?xml version="1.0" encoding="UTF-8"?> ... </kml>';
     var doc2 = '<?xml version="1.0" encoding="UTF-8"?> ... </kml>';
     var doc3 = '<?xml version="1.0" encoding="UTF-8"?> ... </kml>';
     exml.parseString([doc1, doc2, doc3]);

“parsed” Event

EGeoXml throws a “parsed” event when the parsing is complete.

The EgeoXml.parse() operation is asynchronous because it uses GDownloadUrl(), so if you have code that should only operate after the markers etc. have all been created, then you’ll need to listen for that event.

Using (G)MarkerManager

EGeoXml isn’t really suitable for handling large numbers of markers, but using (G)MarkerManager can help speed things up a little.

To use (G)MarkerManager with EGeoXml, create the manager in the normal way, and write a helper function that assigns minZoom and maxZoom settings for each marker. Then use {addmarker: } to tell EGeoXml which function to use.

Whenever EGeoXml has a marker ready to be added to the map, instead of performing map.addOverlay(), it will pass the marker and some extra information to your helper function. Your function should examine the extra information to choose the minZoom and maxZoom settings for the marker and either call manager.addMarker() with that information immediately, or store the marker reference in an array to be used with manager.addMarkers() later when the “parsed” event is triggered.

The passed information is: (marker, title, description, number, iconImage). Your code can examine any of that information for clues about which manager settings to use.

It sounds complicated, but the code is quite simple, see the examples below.

Several pages of My Maps

If your My Map runs to several pages, then each of the XML files will contain data for an individual page.

Pass the URLs of those files in an array, and any overall processing (such as zooming to fit the map view to the data) will be performed on the contents of all the files taken together.

var exml = new EGeoXml(“exml”, map, [“file1.xml”,”file2.xml”,”file3.xml”]); If you use a separate EGeoXml() instance for each file, then the map view will be zoomed to fit the contents of each of those files taken separately. The map ends up being zoomed to fit whichever file request completes last.

Using info window styles

The “titlestyle”, “descstyle” and “directionstyle” options allow you to control the CSS styles of the three parts of the info window contents.

There are two basic ways to use these options. You can either use inline styles, like

   {titlestyle:'style = "color:#FF0000;text-align:center;"'}

or you can set the class attribute so that the info window uses a style from an embedded or external style sheet, like

   {directionstyle:'class = "dstyle"'}

If you set the class for the directions section, you can additionally declare styles for “.dstyle a” or “.dstyle form” to give the links and form within the directions section their own separate style settings.

Using any of these style settings wipes out the default styles that I apply to the corresponding section of the info window. My default style settings do not cascade.

You can, of course, apply your styles individually by editing the KML file. This facility just allows you to apply styles to the contents of all the info windows at once. Style information from the KML file does cascade.

Clever Stuff: I don’t actually limit you to only setting the “class” and “style” atrtributes. If you want to, you can add other attribute settings to the option strings and I’ll just plonk them in.

   {titlestyle:'class= "tstyle" onclick="titleclick()" id="Title"'}

Non-MyMap Icons

EGeoXml uses GIcon properties that use the same size of icon and shadow as the My Maps icons, (32,32) and (59,32). It also guesses the URL of the shadow image using the conventions used by My Maps. That’s fine if your XML file really is a My Maps file, but not if you’re using a XML file from another source that uses very different icons.

KML files don’t specify any information about the icon other than the URLs of the images, so EGeoXml can’t obtain the information from there.

So what you can do is create a GIcon() that contains all the properties for your icons, except the .image, and specify it in the {baseicon} option. EGeoXml will use the settings from the base icon plus the image specified in the XML file.

You should specify at least these properties for your base icon: shadow, iconSize, shadowSize, iconAnchor, infoWondowAnchor.

Printing

Google do not provide printable gif equivalents of the My Map icons.

By default, EGeoXml uses a stretched version of the default marker gif when printing. That’s pretty ugly.

A slightly better alternative is provided via the {printgif:true,printgifpath:”images/”} options.

When these options are set, EGeoXml will extract the filename of the icon, replace “.png” with “.gif” and use the specified path. E.g. if the icon image is “http://maps.google.com/mapfiles/ms/icons/sailing.png&#8221; and the printgifpath is “images/”, then EGeoXml will use “images/sailing.gif” for the print icon.

Both options must be specified. The printgifpath can be relative, like “images/” or absolute, like “http://mydomain.com/images/&#8221;.

A set of gif images matching the My Map png files can be obtained from here: printgifs.zip. To use them, unzip the file and place the gifs in your printgifpath folder.

Custom sidebars and selection boxes

You can now write your own functions for adding entries to the sidebar or drop down selection box.

Your function will be passed:

  1. the name of the global variable that points to the EGeoXml instance
  2. the placemark name
  3. the placemark type (“marker”, “polyline” or “polygon”)
  4. the index into the correspongding gmarker[], gpolyline[] or gpolygon[] array
  5. HTML that’s the same as I use for the polyline or polygon graphic in my sidebars

The drop down selection box currently only supports placemarks of type “marker”.

For example, here’s a function that creates a sidebar entry that’s the same as the EGeoXml default sidebar for markers, but returns an empty string for polylines and polygons:

    function side(myvar,name,type,i,graphic) {
      if (type == "marker") {
        return '<a href="javascript:GEvent.trigger('
          + myvar+ '.gmarkers['+i+'],\'click\')">' + name + '</a><br>';
      }
      return "";
    }

It would be invoked by using {sidebarfn:side}

ELabels

To have ELabels displayed alongside your markers, you need to:

  • Include the elabel.js extension.
  • Create a CSS style class for the label display.
  • Set the {elabelclass} option to specify that sytle class.
  • Optionally specify the {elabeloffset} to control the position of the label relative to the marker.
  • Optionally specify the {elabelopacity}. If omitted, the label will be 100% opaque.

The ELabel parameters are the same as ELabel parameters 3, 4 and 5 described in the Elabel documentation.

Preloading images

If you have images in the <description> field which don’t have a height attribute, then the API guesses that they have zero size and doesn’t make the info window graphics large enough.

The best way to fix that is to add a height attribute to all your images.

An alternative way to fix it is to use {preloadimages:true}. This will cause EGeoXml to attempt to identify all the image files that you use in your <description> fields, and preload them. If you have lots of large images, this will cause EGeoXml to execute rather slowly.

This only fixes the height of the info window. The width of the info window is controlled by the {iwwidth} parameter. If you have images wider than 250 pixels, then you will need to set {iwwidth} to at least the width of your widest image.

Remote Files

A security feature prevents EGeoXml directly reading files that are on other domains, but if your webhost supports server side scripting you can write a proxy server that reads a remote resource and echoes the data to its output stream.

Examples

Normal use with a sidebar

Using your own createMarker function

Using the default marker icon

Polylines

Polygons

Sorted sidebar, directions and non-MyMap icons

Using info window styles

Using GMarkerManager, method 1

Using GMarkerManager, method 2

Printable gifs and a drop down select box

Using ELabels

Using ClusterMarker

Accessing remote resources uses this server to obtain current data from maps.google.com


Third Party Extensions

Google Maps API Tutorial

Using the ELabel extension

The ELabel extension is implemented as a Custom Overlay.

ELabel allows you to place text labels or images or any other HTML elements on the map and have them move with the map, like marker and polyline overlays

Here’s an example

In order to use ELabels you need to:

  1. Create CSS class styles for your labels. It is possible to run ELabels without them, but it’s generally easier to use them.
  2. Include a copy of the “elabel.js” file. Please take your own local copy, don’t hot link to my copy of the file.
  3. Create some ELabel objects.
  4. Use map.addOverlay() to add them to the map.

1. CSS Class Styles

Create CSS class styles for your ELabels to use. E.g.

    <style type="text/css">
    .style1 {background-color:#ffffff;font-weight:bold;border:2px #006699 solid;}
    </style>

You’ll probably find it a lot easier to use pre-defined styles.
You’ll usually want to include a background-color style setting, because the default is to have an invisible background.

2. Include a copy of the “elabel.js” file

Download it from here elabel.js

Include it like

    <script src="elabel.js" type="text/javascript"></script>

3. Create some ELabel objects

E.g.

      var label = new ELabel(new GLatLng(44.3,-78.8), "Utopia", "style1");

The parameters are:

  1. a GLatLng() specifying the location.
  2. a html string to be used as the contents of the ELabel.
  3. (optional) a string containing a class name, corresponding to the class style you created in step 1.
  4. (optional) a GSize() specifying a pixel offset from the GLatLng(). Without this parameter, the bottom left corner of the ELabel will touch the location specified by the GLatLng
  5. (optional) an opacity percentage. If omitted, the ELabel will be 100% opaque.
  6. (optional) set this to true to cause ELabels to overlap intelligently. If omitted, the latest one to be addOverlay()ed will be on top.

4. Use map.addOverlay() to add them to the map.

And use map.removeOverlay() if you want to remove them.

map.clearOverlays() will also remove all the ELabels as well as any markers and polylines.

Notes

ELabels are placed above the markers and below the info window.

You can click markers that are underneath ELabels, even if the marker is 100% opaque.

You can click, double-click and drag parts of the map that are underneath ELabels.

You can use different style classes for different ELabels on the same map.

You don’t need to keep a reference to your ELabels unless you want to removeOverlay() them or change their properties.

The content of an ELabel can be as complex as you want. Here’s an example of slightly more complex content.

Manipulating ELabels

When an ELabel is not addOverlay()ed, you can change its properties directly. e.g.

    map.removeOverlay(label)
    label.pixelOffset=new GSize(20,10);
    map.addOverlay(label)

While a label is addOverlay()ed, the following methods can be used to modify its properties dynamically.

  • label.hide() : Makes the label invisible
  • label.show() : Makes the label visible if it was hidden
  • label.setContents(html) : changes the contents of the label.
  • label.setPoint(glatlng) : changes the location of the label.
  • label.setOpacity(number) : changes the opacity of the label.

Using ELabel with MarkerManager

ELabels work under the Open Source MarkerManager.

The Open Source MarkerManager can be obtained here http://code.google.com/p/gmaps-utility-library-dev/wiki/Libraries.

Now that the Open Source MarkerManager is available, I’m not going to be updating ELabels to work under new releases of the obfuscated GMarkerManager. The problem is that I need to know the internal name by which GMarkerManager requests the location of the overlays that it manages. MarkerManager uses overlay.getPoint(), but the obfuscated GMarkerManager changes the internal function name whenever the code is re-obfuscated.

MarkerManager can manage ELabels in the same way that it can manage GMarkers.

Here’s an example that uses 1576 ELabels.

Markered Labels

If you try to place an ELabel alongside every marker, then you can get the the markers to overlap the markers sensibly and the labels to overlap the labels sensibly, but the labels and markers don’t overlap each other sensibly.

What you can do is place an <img> of the marker inside the ELabel contents, so now the marker image and text acts as a single ELabel, and the intelligent overlapping works.

If you care about how the image looks in MSIE6, then you need to to add code to switch to using the AlphaImageLoader if the browser is MSIE. Or you could use a GIF image instead of a PNG.

If you want the markers to be clickable, then place an invisible GMarker at the same location. ELabels are not clickable, so the click drops through to the GMarker below it.

Here’s an example.

Just for fun, I bump up the opacity of the label when the info window is open on the GMarker associated with it. You could do the same thing with mouseover and mouseout events on the GMarker.

Bugs

IE sometimes seems to return false information about the height of the ELabel contents. This causes the vertical positioning of the ELabel to be incorrect.

Updates

v0.2: 5 April 2006: ELabel.copy() fixed. The labels now get copied correctly into .showMapBlowup()’s.
(Thanks to “Jacob” for pointing out the problem)

v1.0: 7 April 2006: Lots of new methods added

v1.1: 31 Dec 2006: Works with GMarkerManager under API v2.67 to v2.71

v1.2: 25 Feb 2007: Works with GMarkerManager under API v2.67 to v2.75

More advanced stuff

Google Maps API Tutorial

Altitude

Google themselves don’t serve altitude information, so if you want to obtain the altitude, you’ll need to get it from somewhere else.

Here’s a simple example

One place where you can get altitude information is the US Geological Survey Elevation Query. Information about their service can be found here:
gisdata.usgs.gov/XMLWebServices/TNM_Elevation_Service.php

A request looks like this:
http://gisdata.usgs.gov/xmlwebservices2/elevation_service.asmx/getElevation?X_Value=-118.4&Y_Value=36.7&Elevation_Units=METERS&Source_Layer=-1&Elevation_Only=true.

The parameters are:

X_Value Longitude
Y_Value Latitude
Elevation_Units METERS or FEET.
Source_layer You can use this to request data from a specific survey, or use -1 to request the best available data at the location.
Elevation_Only true or false. If you set it false you get information about the data source as well as the elevation data.

The service provides a SOAP interface that returns XML, so you can’t access it directly from Javascript for security reasons. What you have to do is to write a little server script that runs in your own domain. Your Javascript can send a request to your own server, which then sends the request to USGS and returns the reply back to your Javascript client.

I’m not a PHP expert, but I managed to throw together something that works for this purpose. A general purpose relay script would need to be more complicated than this, to avoid the possibility of cross-domain attacks, but I think I can trust the USGS. My PHP code looks like this and returns the data like this:altitude.php?lat=53&lng=-2.

There are two asynchronous steps in this chain, so don’t expect to be able to write a function that returns the altitude of a point. Send the request with GDownloadUrl() and then process the reply in the callback funtion. If you’re sending several requests (e.g. to obtain the profile of a cycle route) you can wrap your call in a function and use it to hold Function Closure so that you can match the replies back to the requests.

The service will return up to 10 decimal places of information, but I suspect that the surveys aren’t accurate to 0.1 nanometres. In my code I just use the integer value.

The service returns the value 0 for open sea. You could therefore use it to distinguish land from sea, but there are a few other points on land where the elevation is also zero, e.g. Elburg in the Netherlands.

The documentation states that the service can return the value -1.79769313486231E+308 if there is no valid data for the requested location, so you might want to filter out such values if you’re drawing an elevation diagram.

In this example I use GDirections to find a route, use the EPoly extension to obtain points at equal distances along that route, use USGS to find the altitude at those points, and use the Google Chart API to plot a chart of those altitudes.

More advanced stuff

Google Maps API Tutorial

Communicating between iframes

I don’t think it’s possible to pass references to complex Objects, like GMarker()s, between iframes. What you can do instead is to have “helper” functions in the target iframe that accepts simple Objects, like Numbers and Strings, and obtains references to the complex objects within that iframe.

Here’s a simple example

This example has two iframes, one for the map and one for the sidebar.

Only frame1 loads the Google Maps API code.
It loads the XML for the markers in the normal way and adds them to the map. It builds up the sidebar contents in the normal way, but instead of putting them into document.getElementById(“side_bar”), it sends the contents to a helper function in frame2. parent.frame2.sideBar(side_bar_html)

The helper function in frame2 takes the passed string and places in its local “side_bar” div.

Once the side bar has been passed, frame2 contains a series of calls like
<a href=”javascript:parent.frame1.myclick(1)”>. These pass a simple number to the “myclick()” helper function that lives in frame1.

Warning

Debugging Javascript that crosses between iframes is significantly harder than debugging Javascript that operates in a single frame.

More advanced stuff

Google Maps API Tutorial

Setting the z-index of markers

It is possible to set the z-index of markers.

You might want to do this if your map tends to be rather cluttered with overlapping markers, and you want to highlight a particular marker, but normally find it to be partially obscured behind less significant ones.

To use the facility, you need to create a little function which will return an integer to be used as the z-index. Markers with higher z-index values are drawn on top of those with lower z-index values.

You can calculate the value from scratch, or you might modify the usual value, which can be obtained fromGOverlay.getZIndex(marker.getPoint().lat()).

The Function is passed two Parameters. The first Parameter is a reference to the marker and the second one always seems to be undefined at present.

Attach your function to the marker by using the GMarkerOption {zIndexProcess:myFunction}

You can’t change the order of the markers afterwards. The Function only gets evaluated when the marker is addOverlay()ed.

I don’t think you can pass a Parameter directly to the zIndexProcess Function, but what you can so is to add a Property to the marker before you addOverlay() it. The function can read that Property and use it in the calculation. For example, if you want important markers to be placed on top, then you can add a marker.importance property to each marker, and return higher z-index values for markers with higher importance.

More advanced stuff

Google Maps API Tutorial

Parameters

Passing and receiving parameters isn’t part of the API itself, but it’s something that you might want to do with your map.

Receiving Parameters

You can add parameters to the end of a URL like this example_linktothis.htm?lat=53.816134&lng=-3.055567&zoom=18&type=k

Using parameters allows you to use the same page to display different information. In this case I’m using the parameters to pass the latitude, longitude, zoom and map type, but you could pass other information in the same way.

The contents of the URL string from the “?” onwards are made available to the Javascript on that page in a variable called “location.search”. The code can scan the information in that variable and perform the actions requested in the parameters.

It is conventional to use the same format as is used by a GET request from a <form>, i.e. a sequence of &argname=value pairs. You don’t actually have to format the information that way as long as long as your Javascript knows how to parse whatever you do send.

In the  example  I perform the following steps:

  1. Set up some default values in case the page has been called without a complete set of parameters.
  2. Remove the “?” from the front of the location.search string.
  3. Break the string into a series of “argname=value” pairs
  4. Break each of those into argname and value.
  5. Perform specific actions for each argname.

The map is then created and setCenter is called using the information received.

The example uses .toLowerCase() to convert the received string to lower case.

Markers

You can use the same mechanism to specify a marker, and have the page centre on that marker or open its info window, like this example_linktomarker.htm?marker=1 or like this example_linktomarker.htm?id=Marker%20Two

If passing an id string that may contain spaces or other punctuation, use unescape() to convert the escaped characters back into normal text.

Sending Parameters

The first example page has a “Link to this page” link.

That link points to a URL that contains parameters that contain the lat, lng, zoom and type settings for the current display.

So, for example, you could right-click that link, copy the link location, and email that URL to someone else. When they use the URL that they receive in their email, the parameters are passed through their browser to the Javascript on the page and the map status is regenerated exactly as it was when you saved the link.

In order for such right-clicks to work, it’s not possible to generate the URL on demand. What we have to do is create the URL every time the map state changes, so that it’s already there when someone right-clicks it.

In my example, I perform the following steps:

  1. Put an empty <div id=”link”></div> in the html.
  2. Write a little function, makeLink(), that reads the status, creates the URL string and builds an <a> link inside that <div>
  3. The makeLink() function is called when the page is opened.
  4. A GEvent listener calls makeLink() again every time the map state changes.

More advanced stuff

Google Maps API Tutorial

GIcon.label

This feature allows you to build your icon from a background image plus a foreground label. Prior to the introduction of this feature, if you wanted to have markers with different letters and different background colours, you had to create a separate icon image for each combination.

There’s an example here.
Observe the way that the marker background and label are fetched separately when the images are not in cache (use shift-reload to see the effect if the images are already in your cache).

The syntax is a bit weird. The GIcon.label is an object of unnamed class that must have the following properties:

url string The URL of the image to be used as a label.
anchor GLatLng() or GPoint() The position offset of the label image relative to the main image.
size GSize() The pixel size of the label image

If you use a GLatLng() for the anchor the parameters are (down, right), but if you use a GPoint() the parameters are (right, down).

The easiest way to set things up is to have the label be the same size as the icon image and set the anchor to GPoint(0,0), but it’s more efficient to make the label smaller and use the size and anchor parameters to position it.

You can set the GIcon.label directly, like this

   var Icon = new GIcon();
   Icon.label = {"url":"overlay.png",
                 "anchor":new GPoint(4,4),
                 "size":new GSize(12,12)};

or you can use the new additional parameter of the GIcon() constructor, like this

   var mylabel = {"url":"overlay.png",
                  "anchor":new GPoint(4,4),
                  "size":new GSize(12,12)};
   var Icon = new GIcon(G_DEFAULT_ICON, "marker.png", mylabel);

Potential Pitfalls

  1. Using a GIcon.label interferes with the click processing of markers that don’t have a .transparent or .imageMap, because the label image would then be the top image in the stack. Therefore, if you use GIcon.label on a clickable label you must set up a .transparent and a .imageMap.

More advanced stuff

Google Maps API Tutorial

Distances

The API provides a function that calculates the straight line distance between two points.

To use it to calculate the distance in metres between two GLatLng objects, we writeP1.distanceFrom(P2).

That returns a number with the maximum number of decimal places that your Javascript interpreter supports, which is somewhat excessive so you might want to use the .toFixed(N) method to round the result to N decimal places.

Here’s an example of the code in use.

For the bearing, I’ve used an algorithm by Vincenty which is claimed to produce better results than using atan2() when the distances are large.

The Basics

Google Maps API Tutorial

Using different languages

Normally, the text used by the API will be in US English.

The API will look through the preferred languages in your browser options and use the first language that Google supports.

If you wish to specify a particular language, then you can add a “hl” parameter to the line that loads the API code, e.g.
src=”http://maps.google.com/maps?file=api&amp;v=2&amp;hl=de&amp;key=abcdefg” 
The official list of supported languages is kept here: code.google.com/support/bin/answer.py?answer=60737&topic=12267

A slightly different, but equally official, list is available on this spreadsheet, in the “localized” column.gmaps-samples.googlecode.com/svn/trunk/mapcoverage_filtered.html

Directions details

When you use GDirections, by default, the route description text will be in the same language as that used in the API as described above.

If you want to use a different one of the supported languages, then you can add the locale option to your .load() or .loadFromWaypoints() call, e.g. gdir.load(“from: Detroit to: Dayton”, {locale:”fr”});.

Other languages

If you want to use a language that’s not yet supported by Google then you can do it like this example. All I’ve done there is replace the Google text with the same text in upper case. To use a real language, you’d need to translate all that text into your chosen language.

The way it works is that the API loader uses GAddMessages() to pass the language specific text strings to the main API code. We can overwrite those text strings with our own GAddMessages() call.

However, the GMapTypes are created before the loader returns control to our code. We can performGAddMessages{10050: ‘SATELLITE’} in our own code, but it’s too late to affect the map type. What we can do, however, is create our own map types which are clones of the Google map types except that they use different texts in the “name” parameter and in the “shortName”, “errorMessage” and “alt” options.

Copyright texts associated with a map type are generated in a server, so they’re not accessible by that method. What we can do is write our own custommap.getCopyrights() method which reads through the prefixes and replaces any occurrences of “Map Data” and “Imagery” with the equivalent in the desired language.

There’s no reasonable way to produce GDirections details in unsupported languages, because that’s performed in the direction server.

The Basics

Google Maps API Tutorial

Sending KML files to Google Maps

Instead of developing your own page with Google Maps API, it’s possible to write a KML file and send it to Google Maps.

Google Maps understands a small subset of KML 2.0, and implements markers, polylines, polygons, info windows and a sidebar.

A simple KML file contains an XML header, a KML header and a <Document>…</Document> structure. That <Document> can contain a series of <Placemark>s which each describe one marker or polyline.

Here’s what a basic XML file looks like

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
<Document>
  <name>KML Example file</name>
  <description>Simple markers</description>

  <Placemark>
    <name>Marker 1</name>
    <description>Some stuff to put in the first info window</description>
    <Point>
      <coordinates>-122.1,37.4,0</coordinates>
    </Point>
  </Placemark>

</Document>
</kml>

The whole file must be syntactically valid XML. The case of the tags is significant.
The Document name is displayed in the title bar and in bold in the sidebar.
The Document description is displayed in the sidebar.
The Placemark name is displayed in the sidebar and in bold in the info window.
The Placemark description is displayed in the info window.
The coordinates are in the order longitude, latitude, altitude. (Note that this is the opposite order from GLatLng() in the Google Maps API) The altitude value is not used.
The map will be automatically centred and zoomed in such a way as to contain all the placemarks.

To send the KML file to Google Maps, put its full URL in the ?q parameter, like this http://maps.google.com/maps?q=http://econym.org.uk/gmap/example1.kml

Marker Icons

That example used the default marker icons. As an alternative you can use the Google Earth icons.

  <Placemark>

    <Style>
      <IconStyle>
        <Icon>
          <href>root://icons/palette-3.png</href>
          <x>64</x>
          <y>32</y>
          <w>32</w>
          <h>32</h>
        </Icon>
      </IconStyle>
    </Style>

    <name>Marker 1</name>
    <description>Some stuff to put in the first info window</description>
    <Point>
      <coordinates>-122.1,37.4,0</coordinates>
    </Point>
  </Placemark>

The <Style>, <IconStyle> and <Icon> tags should be nested as shown.
The <href> indicates which of the four Google Earth palettes the icon is chosen from.
The available palettes are numbered 2, 3, 4 and 5.
The <x> and <y> values indicate the position of the icon within the palette.
Only the values 0, 32, 64, 96, 128, 160, 192 and 224 are permitted.
The <h> and <w> values indicate the size of the icon. The values must always be 32.

The four palettes, eight x values and eight y values allow any of the 256 Google Earth icons to be selected.

Here’s an example

Custom Icons

It is now possible to use your own custom icon images. The images must be on a webserver that the Google server can read, not on your own local disk.

  <Placemark>

    <Style>
      <IconStyle>
        <Icon>
          <href>http://www.domain.com/images/mymarker.png</href>
        </Icon>
      </IconStyle>
    </Style>

Polylines

A polyline can be drawn with the <LineString> Placemark, like this:

  <Placemark>
    <name>Polyline 1</name>
    <description>This is some info about polyline 1</description>

    <Style>
      <LineStyle>
        <color>ff00ff00</color>
        <width>6</width>
      </LineStyle>
    </Style>

    <LineString>
      <coordinates>-122.1,37.4,0 -122.0,37.4,0 -122.0,37.5,0 -122.1,37.5,0 -122.1,37.4,0</coordinates>
    </LineString>
  </Placemark>

Like Point Placemarks, there will be a clickable entry in the sidebar for each LineString Placemark.
The <Style> section is optional. If omitted you get the default polyline style.
The <color> is specified with eight hexadecimal characters, which consists of four pairs. In turn they represent Opacity, Blue, Green and Red.
Here’s an example

Polygons

A filled polyfon can be drawn with the <Multigeometry><Polygon> Placemark, like this:

  <Placemark>
    <name>Polygon 1</name>
    <description>This is some info aboout polygon 1</description>

    <Style>
      <PolyStyle>
        <color>44ff0000</color>
        <fill>1</fill>
        <outline>1</outline>
      </PolyStyle>
    </Style>

    <MultiGeometry>
      <Polygon>
        <outerBoundaryIs>
          <LinearRing>
            <coordinates>-122.1,37.4,0 -122.0,37.4,0 -122.0,37.5,0 -122.1,37.5,0 -122.1,37.4,0</coordinates>
          </LinearRing>
        </outerBoundaryIs>
      </Polygon>
    </MultiGeometry>

  </Placemark>

Like Point Placemarks, there will be a clickable entry in the sidebar for each LineString Placemark.
The <Style><PolyStyle> section is optional. If omitted you get the default polygon style.
Here’s an example

HTML descriptions

A subset of HTML is permitted in the <description> fields. This allows you to insert links and images in the sidebar and info windows.

When doing so, you should use <![CDATA[ ]]> to prevent the contents being interpreted as XML .

  <description><![CDATA[  <a href="http://www.econym.demon.co.uk">Clickable Link</a>  ]]></description>

<span>s and <div>s don’t seem to be accepted.

Here’s an example

Potential Pitfalls

  1. Watch out for the order of the <coordinates> parameters: longitude then latitude.
  2. If you make a change to your KML file, the old copy will be cached on the Google server for a while. Refreshing yourbrowser’s cache won’t force Google to refresh their cache. What you can do is to temporarily add an arbitrary parameter to the end of the url, like “?123”, to make the Google cache think that it’s different.
  3. KML files can’t be sent from your local disk with URLs that begin with “file://” they need to be on a real web server.
  4. If you use an image, or any other resource, in your <description> fields, you must use the full URL.Relative URLs are considered to refer to files on http://www.google.com where the page was loaded from, not from the domain that your KML file came from.

The Basics

Google Maps API Tutorial

Associative arrays and Custom Icons

When I want to manage custom icons with information from XML data, I like to use an associative array.

Javascipt associative arrays can be used just like ordinary arrays, except that they use a string instead of a number as the index.

This means that you can store a meaningful name for the icon type in the XML data, rather than a meaningless number, without having to have a string of if statements to translate the XML attribute into the corresponding GIcon variable name.

E.g. if you’ve got custom icons that represent a house, a school and a factory, you can create an array like this

 var gicons=[]; gicons["house"] = new GIcon( ... ); gicons["school"] = new GIcon( ... ); gicons["factory"] = new GIcon( ... );

and then create your markers like

 new GMarker(point, gmarkers["school"]);

and if you add an attribute to your XML, like icontype=”school” you can read that attribute string from your XML in the normal way, and use it to index the associative array.

 new GMarker(point, gicons[icontype]);

Here’s an example

You can see the XML data file that I used here

Potential Pitfalls

  1. Associative arrays are case specific. gicons[“house”] is not the same as gicons[“House”].

The Basics

Google Maps API Tutorial

Polylines from XML

Here’s an example showing how to read polyline data from an XML file.

Here’s the XML file.

The method is similar to the processing of markers from an XML file, except now each line contains several points. When the data is processed we need to use a nested loop. The outer loop steps through the lines. The inner loop steps through the points for that line.

I’ve put “colour” and “width” attributes in the line entries in the XML file, and used them to control the colour and width of the polylines.

It’s possible to have marker and polyline information in the same XML file. The ‘getElementsByTagName(“marker”)’ bit only reads the markers and the ‘getElementsByTagName(“line”)’ bit only reads the lines.

Potential Pitfalls

  1. An XML file can only have one top level element. In the example, everything is inside the <markers> . . . </markers>. Don’t try to put the markers in <markers> . . . </markers> and the lines in a separate <lines> . . . </lines> element.
  2. The potential pitfalls from Part 3  apply to polylines as well as markers

The Basics

Google Maps API Tutorial

XML

If you want to have dozens of markers on your page, it gets a bit cumbersome to manage them if you code them all in your Javascript as in the previous examples.

The preferred method for handling large numbers of markers is to store the data in an XML file.

It’s also possible to have an application (e.g. mySQL) running on your server which returns XML containing a different selection from the data depending on query information derived from what your users input. I’m not going to cover the mySQL side of things in this tutorial.

Here’s a simple example

You can see the XML data file that I used here

The code used GDownloadUrl() to send a request to read the file.

At some later time, the reply comes back and the callback funtion gets invoked.

Once the data has been read, the callback function can grab a collection of data from the XML tags

   var markers = xmlDoc.documentElement.getElementsByTagName("marker");

then for each of those tags extract the individual attribute fields

   var lat = parseFloat(markers[i].getAttribute("lat"));

Once all the data has been parsed, we can create the markers and the sidebar as in the previous examples.

Note that the code to insert the assembled sidebar information into its <div> must be inside the callback function.

XML attributes strings can’t contain the characters < or >. You have to use &lt; and &gt;, which will get converted to < and > as the XML data is read.

Instead of using XML attributes, it’s possible to lay out your XML data like this:

   <markers>
     <marker lat="43.65654" lng="-79.90138" label="Marker One">
      <infowindow>Some stuff to display in the&lt;br&gt;First Info Window</infowindow>
     </marker>
   </markers>

Or even like this, using CDATA:

   <markers>
     <marker lat="43.65654" lng="-79.90138" label="Marker One">
      <infowindow><![CDATA[
        Some stuff to display in the<br>First Info Window
      ]]></infowindow>
     </marker>
   </markers>

When using CDATA, is is not necessary to use &lt; and &gt; in the contained HTML.

XML formatted like this can be read with GXml.value, like this

   var html = GXml.value(markers[i].getElementsByTagName("infowindow")[0]);

Potential Pitfalls

  1. Be aware that Javascript i/o is asynchronous (so that the browser can get on with doing other things like fetching images if the i/o request takes a while to complete).
    If you’re used to programming languages that wait for i/o to complete, you might tend to put code that uses the data read from the XML file after the “GDownloadUrl()” code. That would be wrong because code placed there would get executed immediately rather than waiting for the data to arrive. Any code that acts on the retrieved data should be placed inside the callback function.
  2. All data returned from XML is considered to be strings of characters. You need to convert your latitude and longitude from strings to floating point numbers by using “parseFloat()”.
    In some circumstances, it might seem that you can get away without doing that, but then things can go horribly wrong later. if the Google code tries to perform arithmetic on values that are not numbers.
  3. Avoid attribute names that are reserved words in either Javascript or HTML.
    In particular “name” and “long” are reserved words.
  4. XML data strings can’t contain the characters &, < or >. You have to use &amp;, &lt; and &gt;, which will get converted to &, < and >.
  5. Don’t have blank lines or spaces before the first tag in your XML file.
  6. Don’t get confused between the array of marker objects “gmarkers[ ]” and the HTMLCollection of marker data “markers[ ]” that gets returned by getElementsByTagName(). You can’t do things like map.addOverlay(marker[i]) to a HTML data element.
  7. If you want to copy my example.xml file, use the “view source” option in your browser, rather than saving it from the normal browser screen. Some browsers display extra formatting characters which you don’t want to have in your copy. All browsers will convert things like &lt; and &gt; into < and > which are not permitted.

The Basics

Google Maps API Tutorial

Markers and info windows

Here’s a simple example

Link to the example and view the source.

Not all browsers support the Google API, and not all users run their browsers with Javascript enabled. So use a <noscript> element to detect the situation where there is no Javascript, and useGBrowserIsCompatible() to detect a non-compatible browser.

The createMarker() function not only sets up the marker and its event handler, but it also causes local copies of the “marker” and “html” variables to be preserved for later use when the event gets triggered. This is a special feature of the Javascript language called “function closure”.

You can use almost any valid html in the info window, as long as the browser can work out the required height and width before rendering it.

If you want to use html that uses quotes, you can use single-quotes in the Javascript string and double-quotes in the contained html.
E.g. createMarker(point,'<image src=”myimage.jpg” width=150 height=100>’);

Potential Pitfalls

  1. The info window contents inherit the formatting from the parent document. E.g. if your map <div> is positioned with <center>, then the info window contents will be centred.
  2. The code that works out the size of the info window is executed before this style inheritance takes place. If your info window contents then inherit styles that change their size, for example font-size, then the info window size may not fit the contents very well.
  3. Don’t attempt to “unroll” the createMarker() function. This doesn’t work.
          var point = new GPoint(-79.90138, 43.65654);
          GEvent.addListener(marker, "click", function() {
            marker.openInfoWindowHtml("Some stuff");
          });
          map.addOverlay(marker);
    
          var point = new GPoint(-78.89231, 43.91892);
          GEvent.addListener(marker, "click", function() {
            marker.openInfoWindowHtml("Some other stuff");
          });
          map.addOverlay(marker);

    What goes wrong is that there’s only one copy of the “marker” variable. When the “click” event gets triggered at a later time, it gets the value that’s in the variable at the time of the call, but you want the value at the time the event handler was set up.

  4. Don’t put your Javascript inside a <table> or <div>, it will work in Firefox, but not in IE.
    Safe places to put your Javascript are either just before the </body>, or in the header in an onLoad() function.
  5. If you do use an onLoad() function, be careful not to call it “onload()” in all lower case, because that’s a reserved word in Firefox.
  6. If you put an image into an info window, you must specify the height and width, so that the browser can calculate the size of the info window before starting to render the image.
  7. If you use tables or divs inside an info window, do specify the width and height. Don’t use “width=100%”, or the browser won’t be able to calculate the size of the window before rendering the table.
  8. Make sure that the id of your map div is unique.
    Things get rather messy if you’ve got an image_map on your page which uses id=”map”, or an anchor that uses name=”map”.
  9. Do test your page in both IE and a browser that supports standards-compliant Javascript, such as Firefox. If you follow this tutorial, then your code should work on both, but it’s easy to miss something and end up with a map that only works in one environment.
    If you want to be confident that your code works in all browsers, you don’t really need to test hundreds of different browsers because most compliant browsers are built around a small number of layout engines. Just test MSIE6, MSIE7, plus one browser that uses each of the compatible layout engines:

    Engine Browsers
    Gecko Firefox, Camino, Mozilla, Netscape, etc.
    Presto Opera, Nintendo, plus forthcoming Adobe browsers
    WebKit Safari, Google Chrome, Arora, Midori, Omniweb, etc.