This is the P2PU Archive. If you want the current site, go to www.p2pu.org!

Online Maps with OpenLayers

Clickable Icons in OpenLayers

Nick Doiron's picture
Mon, 2011-02-14 02:12

Clickable Map Icons


Add comments below, or in the Forums for help: http://p2pu.org/webcraft/node/12927/forums

 

A : Set up your map

 
Make sure you know how to set up an OpenLayers map on http://jsFiddle.net
 
  • You could also start with our map at http://jsfiddle.net/UAxun/46/   On the top menu, click "Run" to test your new code, and "Update" to get a new URL to share with the group 
 
 
 

B: Add your first Point

  • Before you can create an icon, you have to create a Point object
Fortunately, this part isn't any more difficult than it was to create a LonLat object:
 
long = 78.042068;
lat = 27.173768;
myPoint = new OpenLayers.Geometry.Point(long,lat).transform( map.displayProjection,  map.projection);
 
  • As described in the OpenLayers WalkThrough, you're transforming the degrees of latitude and longitude into the measurement system used by the map.  Without this transform, you would need to calculate the position in meters instead of degrees.
 
  • When we stack, mix, and match different sources on top of the map, we call them layers.  Your Point object is going to be added using a new type called a Vector layer.  The OSM layer is made up of many tiles, but this vector layer is made up of points.
 
layerStyle = OpenLayers.Util.extend( { }, OpenLayers.Feature.Vector.style['default']);
pointLayer = new OpenLayers.Layer.Vector("Layer Name", {style: layerStyle});
map.addLayer(pointLayer);
 
  • Some maps libraries let you add a point very quickly, but OpenLayers is precise about things.  You want to customize myPoint before adding it to pointLayer.
 
sampleStyle = OpenLayers.Util.extend( { }, layerStyle);
        sampleStyle.fillColor = "blue";  // the main color of the shape
        sampleStyle.graphicName = "square";  // other good shapes are "star", "circle"
        sampleStyle.pointRadius = 10;  // controls the size of the marker
        sampleStyle.strokeColor = "blue";  // the border color of the shape
        sampleStyle.strokeWidth = 3;  // border width
        sampleStyle.fillOpacity = 1;  // from 0 (transparent) to 1 (opaque)
        sampleStyle.graphicOpacity = 1; // from 0 (transparent) to 1 (opaque)        
pointLayer.addFeatures( [ new OpenLayers.Feature.Vector(myPoint,null,sampleStyle) ] );
 
  • Tah-dah!  Let's see what that looks like (if you don't see anything, make sure the place where you are adding the point is visible on your map!)  http://jsfiddle.net/UAxun/68/
 
  • Adding a second, different-looking point is much easier.  You can copy sampleStyle and make a small adjustment to make rotatedStyle, like this:
 
rotatedStyle = OpenLayers.Util.extend( { }, sampleStyle);
rotatedStyle.rotation = 45;  // 0 to 360 degrees... this doesn't work as well for circles
// create a new point called rotatedPoint
pointLayer.addFeatures( [ new OpenLayers.Feature.Vector(rotatedPoint,null,rotatedStyle) ] );
 
I played around with some style options to get http://jsfiddle.net/UAxun/70/
 

C: Add your first icon

  • Find an icon online
  • Choose "Icon" for size when making a Google Image Search
  • Or create your own icon and upload to http://imgur.com  Use the direct link - i.imgur.com - as your icon URL
 
  • An icon is no different from any other style option.  Go back to when you created sampleStyle and add this line to the end:
 
 
  • My icon looked small.  Just like with the original point object, increase the pointRadius to change the size
 
sampleSize.pointRadius = 24;
 
Notice that when you change sampleSize, it also affects all styles you copied from it, so the rotatedStyle points also have this icon: http://jsfiddle.net/UAxun/71/
 

D: Multiple icons

  • Use the libraries listed above to find more icons for your map
  • Create one standardStyle with the size, transparency, and other things that you want
  • Copy standardStyle for each different icon, using the code
 
dolphinIconStyle= OpenLayers.Util.extend( { }, standardStyle);
dolphinIconStyle.externalGraphic = "http://google-maps-icons.googlecode.com/files/dolphins.png";
 

E: Attributes

 
For awhile, we've been using this line to add a point:
 
pointLayer.addFeatures( [ new OpenLayers.Feature.Vector(rotatedPoint,null,rotatedStyle) ] );
 
  • Let's break this down to two lines:
 
// create myPoint and myStyle before this line
myPointFeature = new OpenLayers.Feature.Vector(myPoint,null,myStyle)
pointLayer.addFeatures( [ myPointFeature ] );
 
  • If you're not familiar with JavaScript, here's a quick explainer.  Curly brackets create an object, which are essentially a list of properties.  Place a comma after each item in the list, except for the last.  For example:
 
osm = { fullName: "OpenStreetMap", isOpen: true, homepage:"http://openstreetmap.org" } ;
bng = { fullName: "Bing Maps", isOpen: false, homepage:"http://maps.bing.com" } ;
 
Now we can check these properties. For example, osm.isOpen = true, and window.location=bng.homepage will send your browser to maps.bing.com
 
  • Go back to your maps code, and add some custom properties to myPointFeature.
 
myPointFeature = new OpenLayers.Feature.Vector(myPoint,null,myStyle)
myPointFeature.attributes = { 
      name: "Taj Mahal",
      description: "One of India's most famous buildings",
};
pointLayer.addFeatures( [ myPointFeature ] );
 
  • Now each of your points is a lot smarter.  It knows what its name is, can describe itself, and can give you a link to a page on Wikipedia.  Now we just need a way to know if the user is asking for this information, by clicking on that icon.
 

F: Clicks and good popups make an interactive map

 
  • Okay.  A couple more leaps of faith and you will have an interactive map.
  • I assume your layer is still called pointLayer.  Always double-check when you use someone else's variable names.
  • A SelectFeature control handles the difficult part of finding when someone is clicking on an icon, and what that icon is.  Then you set up responses.
 
// create a Control that watches for clicking on a feature, then add the control to the map
selectControl = new OpenLayers.Control.SelectFeature( pointLayer );
map.addControl(selectControl);
selectControl.activate();
 
/* the control calls events (featureselected and featureunselected) and we connect those events to functions */
pointLayer.events.on({
    'featureselected': onFeatureSelect,
    'featureunselected': onFeatureUnselect
});
 
  • onFeatureSelect and onFeatureUnselect are functions. Instead of running our code from top to bottom, these functions will not run until something (our clicks on the map) call them.
  • We're going to make the map create a popup window and show the name of a place.  Remember you added a name in the last section about Attributes
 
function onFeatureSelect(clickInfo) {
        clickedFeature = clickInfo.feature;
        popup = new OpenLayers.Popup.FramedCloud(
                "featurePopup",  // an ID for this popup
                clickedFeature.geometry.getBounds().getCenterLonLat(), // anchor to the icon
                new OpenLayers.Size(120,250),  // set height and width of the window
                clickedFeature.attributes.name,  // HTML displayed inside the window
                null,                 // used for other applications
                true,                 // have an [x] to close the Popup
                onPopupClose  // call another function when the Popup is closed
        );
        // let feature and popup 'know' about each other
        clickedFeature.popup = popup;
        popup.feature = clickedFeature;
        // add the popup to the map
        map.addPopup(popup);
}
function onFeatureUnselect(clickInfo) {
        feature = clickInfo.feature;
        if (feature.popup) {
                // if an icon with a popup window is un-selected, that popup is removed
                popup.feature = null;
                map.removePopup(feature.popup);
                feature.popup.destroy();
                feature.popup = null;
        }
}
function onPopupClose(closeInfo) {
        // closing the Popup un-selects the icon
        selectControl.unselect(this.feature);
}
 
  • This is a lot of code to take on faith, but most of the difficult parts of understanding clicks and designing the window is done for you.
  • I'm going to focus on just one clickable icon for now.  I went back to my example and removed the other icons, to get this:  http://jsfiddle.net/UAxun/72/
 

Comment below or go to the forums for help: http://p2pu.org/webcraft/node/12927/forums

 
Once you have that working...
 
  • The line after setting the Popup's size is the line setting what appears inside:
 
clickedFeature.attributes.name,
 
  • This can be any HTML.  Take this line and do something cool with it. For example, let's have the name be a bold red section - <h3></h3> - and then write the description.
 
"<h3 style='font-weight:bold;color:red;'>" + clickedFeature.attributes.name + "</h3>" + clickedFeature.attributes.description,
 
Make sure that the line still ends with a comma.
 
If things go wrong, use your browser's built-in debugger to see if you used the wrong variable name or punctuation.
 

Here are some examples:

 
 
<a href="http://example.com?TheLinkURL" target="_blank">Link Text</a>
 
  • Can you make it show an image?  What about images for two separate markers? The HTML for adding an image is:
 
 

Comments

Here's mine -- using a

Joyce Carpenter's picture
Joyce Carpenter
Thu, 2011-02-17 23:02

Here's mine -- using a library icon and the popup includes a clickable link to the library's website.

http://jsfiddle.net/jcarpenter/URrK3/1/embedded/result/

That was fun. Thanks for the great instructions!

Can't seem to get a second

Sharon Machlis's picture
Sharon Machlis
Sat, 2011-02-19 01:08

Can't seem to get a second item to display on the map, not sure why. http://jsfiddle.net/sharon123/j2Ppj/8/

It's almost working, just a

Nick Doiron's picture
Nick Doiron
Sat, 2011-02-19 03:59

It's almost working, just a typo. When you create restaurantStyle, you're missing the capital O in OpenLayers.

It's possible to find these using a JavaScript console. We'll have posts about these this weekend. If you have Google Chrome, you can press Ctrl+Shift+J to see the code and any errors. In Firefox 4, it's Ctrl+Shift+K. Firefox 3 or earlier, you should install the addon http://getfirebug.com

I tried to make a map of the

Mita Williams's picture
Mita Williams
Sun, 2011-02-20 05:02

I tried to make a map of the all the branches of our public library system in my map, but I stopped once I realized that the linking function only worked with the last set of points in the because there's only one variable set for the url: http://jsfiddle.net/copystar/BpQvj/2/

I'm sure there is an efficient way of fixing things with an array or class or something, but I'm too tired to tackle that at the moment. I'm still very proud of my map!

Mita - I would suggest

Andrew Black's picture
Andrew Black
Sun, 2011-02-20 10:10

Mita - I would suggest refactoring the repeated code in to a function.
The trap I think you have fallen into (and I have done in the past) is that the variable mypoints is reused and "looses" the attributes you set for it. If it is is a function this will create a new variable.
Sorry - rather a quick reply as I need to go out soon. Will try to add more info later.

Mita, don't worry, you won't

Nick Doiron's picture
Nick Doiron
Sun, 2011-02-20 16:18

Mita, don't worry, you won't need to use arrays or classes just yet. You can take out some lines of code and it'll work perfectly.

Here's the process:

1) Create new layerStyle
2) Create new pointLayer (for all points)
3) Add pointLayer to map
4) Create new sampleStyle (for several points)
5) Create new point
6) Create pointFeature based on point and sampleStyle
7) Add attributes to pointFeature
8) Add pointFeature to pointLayer

In your code, you jump back to the beginning, and create new layers. Only the last layer (with the last point) is being sent to the SelectControl, so only that one works. Instead, go back to Step 5 to create a new point and add it to the same layer. Or go back to Step 4 if you're changing the style. So you want to have these lines only once, at the beginning:

layerStyle = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']);
pointLayer = new OpenLayers.Layer.Vector("Layer Name", {style: layerStyle});
map.addLayer(pointLayer);

And this line is unnecessary (here you add myPoint to pointLayer, before it's made into myPointFeature - that's something we used in the code before we were adding attributes)

pointLayer.addFeatures( [ new OpenLayers.Feature.Vector( myPoint,null,sampleStyle) ] );

Take those out, and it works completely!

Thanks so much. I made the

Mita Williams's picture
Mita Williams
Mon, 2011-02-21 05:19

Thanks so much. I made the changes you suggested and it does work completely! : http://jsfiddle.net/copystar/BpQvj/4/

The 8 step process you outlined above helped me get a better understanding what my copying and pasting was doing. So much so, that I got ambitious and tried to clean up my code and somehow lost my icons in the process :/ http://jsfiddle.net/copystar/e67Xj/

Apologies - I somehow got my

Mita Williams's picture
Mita Williams
Mon, 2011-02-21 05:42

Apologies - I somehow got my jsfiddles mixed up. Will update on my progress with my cleaned up code later.

I've cleaned up the code and

Mita Williams's picture
Mita Williams
Wed, 2011-02-23 04:00

I've cleaned up the code and now I have a very nice map of all the branches of the Windsor Public Library system. Thanks!
http://jsfiddle.net/copystar/reKRr/1/

Ah, thanks. Note to self:

Sharon Machlis's picture
Sharon Machlis
Sat, 2011-03-05 22:14

Ah, thanks. Note to self: JavaScript is case sensitive. Case Sensitive. CASE SENSITIVE.

Finally got it working: http://jsfiddle.net/sharon123/j2Ppj/24/

I finally have something to

Randy Rosso's picture
Randy Rosso
Wed, 2011-03-16 04:28

I finally have something to show here... it isn't much, because I'm still just working with 3 points. Next up: tackling reading in a whole file of points instead of hand-entering them. But here's something:

http://publicdatapublic.org/openlayers_dc.html