Creating Your Own Overlay Types

Tags: Tutorial, Google Maps, Custom Overlay

I previously wrote a post about how to create a custom map type. It does require a slight knowledge of JavaScript, since you will be accessing the underlying API or custom clientside scripts.

Since the request was in relation to a custom overlay type called LabeledMarker, I decided to write this post based on that. However at the bottom I will go through how you can create an overlay derived from the GoogleOverlay class. Be aware that the code presented here is not guaranteed to work, please report any errors you may experience.

The first thing to do if you want to do if you are using libraries outside the code Google Maps API is to ensure that the library is loaded on your page. There are several ways to do this, either manually include the script tag on your page (remember to put it either in the head or at the top of the page, so it is available when the map script runs), or programatically by using either the ClientScriptManager or the ScriptManager. Of course it goes without saying that you should not hot link to the LabeledMarker library :-)

Creating the Overlay

In the JavaScript library you can see that the LabeledMarker itself is derived from the GMarker, so it makes sense to start our implementation by creating a class that inherits from the GoogleMarker class (the .NET equivalent). This means that we don't have to write a lot of code to add all the functionality of a marker. The main difference is that the LabeledMarker takes a LabelText value in the constructor. In principle we could grab that value from the GoogleMarker's MarkerText property, but I decided to add an extra property (LabelText) for the purposes of this tutorial. The icon value already exists for the GoogleMarker, so we will continue to use that.

The LabelText property is like any other .NET property, but note that for state preservation purposes you will need to write your own override for the XML serialization, so that the value is not lost between page roundtrips.

Finally we come to the tricky part, which is writing the code that will generate the JavaScript which renders the control on the page.  The JavaScript declaration looks like this:

JavaScript

var marker = new LabeledMarker(myPoint, {icon: myIcon, labelText: "A"});

So the RenderString method must output at least this code. However the map control needs a little extra information to keep track of the overlays, so we need to add some more code to add an ID property to the clientside overlay. The overridden RenderString method will look something like this:

C#

public override string RenderString(GoogleMap Map)
{
	return string.Format("var {0} = new LabelMarker(new {1}, {{ \"icon\" : reimers.map.{2}.getIcon(\"{3}\"), \"labelText\" : \"{4}\" }}); {0}.ID = \"{0}\"; ",
		ClientSideID(Map),
		Point.ToString(),
		Map.ID,
		Options.Icon.IconMapID,
		LabelText);
}

As you can see output is basically the same as the JavaScript snippet above, but there are a few things to notice. After the clientside LabeledMarker is instantiated an ID property is added, which is the same as the variable name. This is in order to maintain varaible references. The actual variable name is generated by the ClientSideID method, so you don't need to worry too much about about the naming that of variables.

The JavaScript for the marker point is taken from the GoogleLatLng class, but 'new' is prepended, since it is not know by teh client.

The icon is found using the reimers.map.[MapID].getIcon clientside function. The icons that you create on the server are stored and referenced on the client by the control, but you can access the icon collection yourself using this function. Yes there is an equivalent getOverlay function.

The LabelText property is added to the output. Note that in the property accessor I manually escape double quotes, so that you don't run into trouble with unwanted string terminations.

This is basically it. A sample class code could look like this:

C#

using System;

namespace Reimers.Map.Extensions
{
    public class LabeledMarker : GoogleMarker
    {
        private string lText = string.Empty;

        public string LabelText
        {
            get { return lText.Replace("\"", "\\\""); }
            set { lText = value; }
        }

        public override string RenderString(GoogleMap Map)
        {
            return string.Format("var {0} = new LabelMarker(new {1}, {{ \"icon\" : reimers.map.{2}.getIcon(\"{3}\"), \"labelText\" : \"{4}\" }}); {0}.ID = \"{0}\"; ",
                ClientSideID(Map),
                Point.ToString(),
                Map.ID,
                Options.Icon.IconMapID,
                LabelText);
        }

        public override void ReadXml(System.Xml.XmlReader reader)
        {
            //This should be overridden to ensure that the label text
            //is preserved by the state management.
            base.ReadXml(reader);
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            //This should be overridden to ensure that the label text
            //is preserved by the state management.
            base.WriteXml(writer);
        }
    }
}

If you are more courageous and want to create your overlay from scratch, i.e. an overlay that derives directly from the GOverlay class then you must write an equivalent .NET class that inherits from the GoogleOverlay class.

The GoogleOverlay class has a number of abstract methods that you need to handle. There are the ones described above. The XML serialization is self-explanatory and the RenderString should output the JavaScript code necessary to render the control.

When inheriting directly from the GoogleOverlay class you must also override the ClientSideID method, which is the method that generates the ID used clientside to reference the overlay. You need to ensure that the variable does not use characters that have special meaning in JavaScript.

Happy customization.

Latest Tweets