Dot, dot, comma, dash - a smiley face in a flash :-)

This is the second post of my OpenLayers 3 series. In my last post I have introduced OpenLayers 3. Now I will show you how you can draw on the map. I will start with simple geometries like Point, Circle and Line. Starting from now on I will also use Twitter Bootstrap for layouting the sample.

Create your own map

Let’s start with updating the original example. Instead of using the basic map class of OpenLayers 3 I will create my own map class and inherit from the base class ol.Map.For inheritance OpenLayers 3 provides a simple framework-specific JavaScript method. Because of the inheritance chain I have to call the constructor of the base class. This ensures that all magic that happen in the constructor of the original map class also will be applied on my class .The Google Closure Library help me with this duty. With goog.base I can easily call the base class constructor. You will also see that the code of the previous example to create the map and locate the users position is still there, only inside my own constructor. I inherit from the base because now I can add my own functions to enable drawing to the map class.

example.Map = function Map(elementTarget) {
    this.drawingLayer_ = undefined;
    this.drawingEnabled_ = false;
    this.drawingInteraction_ = undefined;

    goog.base(this, {
        target: elementTarget,
        layers: [
            new ol.layer.Tile({
                // Using OpenStreetMap to show a map
                source: new ol.source.OSM()
            })
        ],
        view: new ol.View({
            // Set the default center of the map view
            center: ol.proj.fromLonLat([0, 0]),
            // Set default zoom
            zoom: 4
        })
    });

    var that = this;

    // Using OpenLayers Geolocation to locate the current user 
    // position using native HTML5 GeoLocations.
    var geolocation = new ol.Geolocation({
        // Get the current map projection
        projection: that.getView().getProjection(),
        // Track the user position
        tracking: true,
        // Tracking options. Timeout = maximum time to locate the user, 
        // maxAge = caching time
        trackingOptions: {
            timeout: 5000,
            maxAge: 300000
        }
    });

    goog.events.listen(geolocation, goog.events.EventType.CHANGE, 
    function (event) {
        that.getView().setCenter(geolocation.getPosition());
        that.getView().setZoom(10);
    });
};
// Inherit from the OpenLayers 3 map
ol.inherits(example.Map, ol.Map);

But be careful. You have to call the inherits() function after defining the constructor of your class, otherwise your code will crash.

Add a drawing layer

In OpenLayers 3 you should not draw directly on your map layer because you don’t want to manipulate the original map data. So I create a new layer only for geometries if I enable my drawing feature. For geometries you need a vector source because they are stored as vectors. For more information about vectors take a look here.
Remember from my first post: Every layer needs a source to know where the data is coming from. In case of the OpenStreetMap it was ol.source.OSM. For the geometries we are going to draw we have to use a vector source: ol.source.Vector.

/**
 * Enables or disables the draw interaction
 * @param {boolean} enabled
 * @return {boolean}
 */
example.Map.prototype.setDrawingEnabled = function (enabled) {
    goog.asserts.assertBoolean(enabled, 'enabled should be a boolean');

    this.drawingEnabled_ = enabled;
    if (this.drawingEnabled_ && !this.drawingLayer_) {
        // Create a new layer for drawing
        this.drawingLayer_ = new ol.layer.Vector({
            // Geometries will be stored as vectors
            source: new ol.source.Vector()
        });

        // Add the new layer for drawing
        this.addLayer(this.drawingLayer_);
    }
    else {
        this.removeInteraction(this.drawingInteraction_);
    }
    return this.drawingEnabled_;
};

You can call the setDrawingEnabled function where you want on an instance of the `example.Map class. I call this function in my app.js when clicking the “Enable drawing” button.

function toggleDrawing(element) {
    var enabled = map.setDrawingEnabled(!map.getDrawingEnabled());
    if (!enabled) {
        // Reset the UI
        resetDrawingTypeListElements();
    }
    element.innerHTML = enabled ? 'Disable drawing' : 'Enable Drawing';
}

Activating drawing

If you activate the drawing you have created a new layer where the geometry data is stored. But till now you can’t draw on it. OpenLayers 3 offers different interactions you can use on your map. An interaction is an action you can perform on the map. One of these interactions is the draw interaction that we will use to draw geometries on our map. I have created a function setDrawingType on my map class that creates an interaction for the chosen geometry (Point, Line, Circle). To create such interaction there are only two parameters necessary: source and type. The source is the same source we set on the drawing layer before. The type we choose depends on the geometry we selected on the UI. OpenLayers 3 has different geometry types. In my example I will only use Point, Line respectively LineString and Circle.

/**
 * Set the type of the geometry that has to be drawn.
 * If param is null the draw interaction will be reset.
 * @param {string|null} type
 */
example.Map.prototype.setDrawingType = function (type) {
    // Remove the old drawing layer if no drawing type is given
    if (type === null) {
        this.setDrawingEnabled(false);
        this.removeLayer(this.drawingLayer_);
        this.drawingLayer_ = undefined;
        this.removeInteraction(this.drawingInteraction_);
        return;
    }


    var drawingType;
    // Choose the geometry type you will use for drawing
    switch (type) {
        case example.Map.DrawingType.POINT:
            drawingType = ol.geom.GeometryType.POINT;
            break;
        case example.Map.DrawingType.LINE:
            drawingType = ol.geom.GeometryType.LINE_STRING;
            break;
        case example.Map.DrawingType.CIRCLE:
            drawingType = ol.geom.GeometryType.CIRCLE;
            break;
        default:
            // Fail if no drawing type matches
            goog.asserts.fail('Unknown drawing type: %s', type);
    }

    // Remove previous interaction
    this.removeInteraction(this.drawingInteraction_);

    // Create the new draw interaction
    this.drawingInteraction_ = new ol.interaction.Draw({
        source: this.drawingLayer_.getSource(),
        type: drawingType
    });

    // Add the interaction to the map
    this.addInteraction(this.drawingInteraction_);
};

At least the interaction has to be added to the map. In our case we can call addInteraction on this because this is our instance of our own map class that inherits from ol.map. To enable the draw interaction we only have to call map.setDrawingType(type);.

Happy drawing

And now: Dot, dot, comma, dash – a smiley face in a flash!

OpenLayers 3 drawing

You can find the example on GitHub.

In the next blogpost I am going to show you how you can style the drawn features and make the architecture more flexible.

Have fun drawing smileys :-) and stay tuned.

About Steffen Jahr

Hi. I'm Steffen from Karlsruhe.
I am working as software developer @ awesome Thinktecture AG. Next to software development I'm addicted to Star Wars :)