Skip to main content
Back
Tutorials

Build Web GIS Applications with OpenLayers: A Complete Tutorial

Akonia Codex Team Akonia Codex Team
June 13, 2026
3 min read
Build Web GIS Applications with OpenLayers: A Complete Tutorial

Building interactive web maps used to mean choosing between complex desktop software or limited online tools. OpenLayers changed that—it's a powerful JavaScript library that brings professional GIS capabilities to any web browser.

Whether you're building location-based services, environmental dashboards, or asset tracking systems, OpenLayers provides the tools to create sophisticated web mapping applications.

What is OpenLayers?

OpenLayers is a high-performance, feature-packed library for creating interactive maps on the web. Unlike simpler mapping libraries, it supports:

  • Multiple map projections (not just Web Mercator)
  • Hundreds of data sources (OSM, Mapbox, WMS, WFS, GeoJSON, KML)
  • Advanced interactions (editing, drawing, measuring, geocoding)
  • Mobile support with touch gestures
  • No API keys required for OpenStreetMap

Getting Started: Your First OpenLayers Map

Let's build a basic interactive map from scratch. Create an index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My First OpenLayers Map</title>
    <style>
        #map {
            width: 100%;
            height: 600px;
        }
    </style>
    <!-- OpenLayers CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/ol.css">
</head>
<body>
    <div id="map"></div>
    
    <!-- OpenLayers JS -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ol.js"></script>
    <script>
        // Initialize the map
        const map = new ol.Map({
            target: 'map',
            layers: [
                new ol.layer.Tile({
                    source: new ol.source.OSM() // OpenStreetMap
                })
            ],
            view: new ol.View({
                center: ol.proj.fromLonLat([0, 0]), // Start at [0,0]
                zoom: 2
            })
        });
    </script>
</body>
</html>

Open this in a browser, and you'll see a full interactive OpenStreetMap.

Adding Markers and Points of Interest

Real applications need to show specific locations. Here's how to add markers:

// Create a marker feature
const marker = new ol.Feature({
    geometry: new ol.geom.Point(
        ol.proj.fromLonLat([-74.006, 40.7128]) // New York coordinates
    ),
    name: 'New York City',
    population: 8336817
});

// Style the marker
const markerStyle = new ol.style.Style({
    image: new ol.style.Circle({
        radius: 10,
        fill: new ol.style.Fill({ color: '#03FF93' }),
        stroke: new ol.style.Stroke({
            color: '#FFFFFF',
            width: 2
        })
    })
});

marker.setStyle(markerStyle);

// Create a vector layer to hold the marker
const vectorSource = new ol.source.Vector({
    features: [marker]
});

const vectorLayer = new ol.layer.Vector({
    source: vectorSource
});

// Add the marker layer to the map
map.addLayer(vectorLayer);

// Center the map on the marker
map.getView().setCenter(
    ol.proj.fromLonLat([-74.006, 40.7128])
);
map.getView().setZoom(10);

Loading GeoJSON Data

Most real applications load data from external files. Here's how to load and display GeoJSON:

// Create a vector source with GeoJSON format
const geojsonSource = new ol.source.Vector({
    url: 'data/pois.json', // Path to your GeoJSON file
    format: new ol.format.GeoJSON()
});

// Create a styled layer
const geojsonLayer = new ol.layer.Vector({
    source: geojsonSource,
    style: new ol.style.Style({
        image: new ol.style.Circle({
            radius: 8,
            fill: new ol.style.Fill({ color: '#FF6B6B' }),
            stroke: new ol.style.Stroke({
                color: '#FFFFFF',
                width: 2
            })
        }),
        stroke: new ol.style.Stroke({
            color: '#FF6B6B',
            width: 2
        }),
        fill: new ol.style.Fill({
            color: 'rgba(255, 107, 107, 0.2)'
        })
    })
});

map.addLayer(geojsonLayer);

Your pois.json file should look like this:

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [-74.006, 40.7128]
            },
            "properties": {
                "name": "New York City",
                "type": "city"
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [-118.2437, 34.0522]
            },
            "properties": {
                "name": "Los Angeles",
                "type": "city"
            }
        }
    ]
}

Creating Custom Popups

Let users interact with your map features by adding popups:

<!-- Add this to your HTML -->
<div id="popup" class="ol-popup">
    <a href="#" id="popup-closer" class="ol-popup-closer"></a>
    <div id="popup-content"></div>
</div>

<style>
.ol-popup {
    position: absolute;
    background-color: white;
    box-shadow: 0 1px 4px rgba(0,0,0,0.2);
    padding: 15px;
    border-radius: 10px;
    border: 1px solid #cccccc;
    bottom: 12px;
    left: -50px;
    min-width: 280px;
}

.ol-popup:after, .ol-popup:before {
    top: 100%;
    border: solid transparent;
    content: " ";
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
}

.ol-popup:after {
    border-top-color: white;
    border-width: 10px;
    left: 48px;
    margin-left: -10px;
}

.ol-popup:before {
    border-top-color: #cccccc;
    border-width: 11px;
    left: 48px;
    margin-left: -11px;
}

.ol-popup-closer {
    text-decoration: none;
    position: absolute;
    top: 2px;
    right: 8px;
}

.ol-popup-closer:after {
    content: "✖";
}
</style>
// Create popup overlay
const popup = new ol.Overlay({
    element: document.getElementById('popup'),
    positioning: 'bottom-center',
    stopEvent: false,
    autoPan: {
        animation: {
            duration: 250
        }
    }
});

map.addOverlay(popup);

// Add click handler
map.on('singleclick', function(evt) {
    const feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
        return feature;
    });

    if (feature) {
        const coordinates = feature.getGeometry().getCoordinates();
        const content = `
            <h3>${feature.get('name') || 'Feature'}</h3>
            <p>Population: ${feature.get('population')?.toLocaleString() || 'N/A'}</p>
        `;
        
        document.getElementById('popup-content').innerHTML = content;
        popup.setPosition(coordinates);
    } else {
        popup.setPosition(undefined);
        closer.blur();
    }
});

// Add closer button
const closer = document.getElementById('popup-closer');
closer.onclick = function() {
    popup.setPosition(undefined);
    closer.blur();
    return false;
};

Map Controls and Interactions

OpenLayers includes built-in controls for common interactions:

import { defaults as defaultControls } from 'ol/control';
import ScaleLine from 'ol/control/ScaleLine';
import FullScreen from 'ol/control/FullScreen';

const map = new ol.Map({
    target: 'map',
    controls: defaultControls().extend([
        new ScaleLine({
            units: 'metric'
        }),
        new FullScreen()
    ]),
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        })
    ],
    view: new ol.View({
        center: ol.proj.fromLonLat([0, 0]),
        zoom: 2
    })
});

// Add drawing interaction
const drawSource = new ol.source.Vector();
const drawLayer = new ol.layer.Vector({
    source: drawSource,
    style: new ol.style.Style({
        stroke: new ol.style.Stroke({
            color: '#03FF93',
            width: 3
        }),
        fill: new ol.style.Fill({
            color: 'rgba(3, 255, 147, 0.2)'
        })
    })
});

map.addLayer(drawLayer);

const draw = new ol.interaction.Draw({
    source: drawSource,
    type: 'Polygon'
});

map.addInteraction(draw);

// Listen for draw end
draw.on('drawend', function(event) {
    const feature = event.feature;
    console.log('Drawn feature:', feature.getGeometry().getCoordinates());
});

Building a Complete Web GIS Application

Here's a complete example combining all features:

// Initialize map with custom controls
const map = new ol.Map({
    target: 'map',
    controls: defaultControls().extend([
        new ScaleLine({ units: 'metric' }),
        new FullScreen()
    ]),
    layers: [
        // Base layer
        new ol.layer.Tile({
            source: new ol.source.OSM()
        }),
        // Marker layer
        new ol.layer.Vector({
            source: new ol.source.Vector({
                url: 'data/locations.json',
                format: new ol.format.GeoJSON()
            }),
            style: new ol.style.Style({
                image: new ol.style.Circle({
                    radius: 8,
                    fill: new ol.style.Fill({ color: '#03FF93' }),
                    stroke: new ol.style.Stroke({
                        color: '#FFFFFF',
                        width: 2
                    })
                })
            })
        })
    ],
    view: new ol.View({
        center: ol.proj.fromLonLat([-98.5795, 39.8283]), // Center of USA
        zoom: 4
    })
});

// Add popup interaction
const popup = new ol.Overlay({
    element: document.getElementById('popup'),
    positioning: 'bottom-center',
    autoPan: true
});
map.addOverlay(popup);

map.on('singleclick', function(evt) {
    const feature = map.forEachFeatureAtPixel(evt.pixel, f => f);
    if (feature) {
        const coords = feature.getGeometry().getCoordinates();
        const content = `
            <h3>${feature.get('name')}</h3>
            <p>${feature.get('description') || ''}</p>
        `;
        document.getElementById('popup-content').innerHTML = content;
        popup.setPosition(coords);
    } else {
        popup.setPosition(undefined);
    }
});

OpenLayers vs Leaflet: Which Should You Choose?

Both libraries are excellent, but they excel in different areas:

Choose OpenLayers if:

  • You need advanced GIS projections
  • You're working with complex geospatial formats
  • You need editing and drawing tools
  • You require professional-grade features

Choose Leaflet if:

  • You want simplicity and ease of use
  • You need a lightweight solution
  • Your requirements are basic mapping
  • You prefer a simpler API

For most production GIS applications, OpenLayers provides the comprehensive feature set needed for professional implementations.

Best Practices for Web GIS Development

1. Use Web Workers for Large Datasets

// Process large GeoJSON files in a web worker
const worker = new Worker('geojson-processor.js');
worker.postMessage({ file: 'large-dataset.json' });
worker.onmessage = function(event) {
    const features = event.data;
    vectorSource.addFeatures(features);
};

2. Implement Clustering for Many Points

const clusterSource = new ol.source.Cluster({
    distance: 40,
    source: new ol.source.Vector({
        url: 'data/many-points.json',
        format: new ol.format.GeoJSON()
    })
});

const clusterLayer = new ol.layer.Vector({
    source: clusterSource,
    style: function(feature) {
        const size = feature.get('features').length;
        return new ol.style.Style({
            image: new ol.style.Circle({
                radius: 10 + (size * 0.5),
                fill: new ol.style.Fill({ color: '#03FF93' })
            }),
            text: new ol.style.Text({
                text: size.toString(),
                fill: new ol.style.Fill({ color: '#FFFFFF' })
            })
        });
    }
});

3. Optimize Performance

  • Use vector tiles instead of GeoJSON for large datasets
  • Implement feature loading on demand
  • Cache frequently accessed data
  • Use WebGL renderer for better performance

Learning Modern GIS Development

Web GIS development combines traditional cartography with modern web technologies. Mastering OpenLayers opens doors to:

  • Location-based services: Track assets, visualize logistics
  • Environmental monitoring: Display sensor data, track changes
  • Urban planning: Visualize zoning, demographics, infrastructure
  • Emergency response: Real-time incident mapping

But building production GIS applications requires more than just library knowledge—you need to understand geospatial concepts, data management, and performance optimization.

Want to Build Production GIS Applications?

We teach Web GIS development in our comprehensive training program. You'll learn:

  • Advanced OpenLayers techniques
  • Geospatial data management
  • Performance optimization for large datasets
  • Integration with backend GIS services
  • Building production-ready GIS applications

Next step: Join our Web GIS Development course and learn to build professional mapping applications with OpenLayers and modern web technologies.

#Web GIS#OpenLayers#mapping#tutorial#javascript#geospatial#GIS development

Share this article

Stay Updated

Get the latest development tips delivered to your inbox.

Related Insights

Swipe →