Search code examples
openlayers

Draw Circles in OpenLayers with Lat/Lon and Radius


I am plotting IGC tracks and want to add circles based on certain checkpoints on the route. I tried using this solution: Draw a circle with defined diameter in OpenLayers

but can't figure out how to implement it in a code such as this one: https://openlayers.org/en/latest/examples/igc.html

Essentially, something like this: enter image description here

How do I add the circle?


Solution

  • I was able to plot the circles. I figured out that I needed more structure to the code to add the circles:

    1. Define the circle
    2. Add circle feature
    3. Create a layer
    4. Add the layer to the map

    I followed an additional example here: http://www.acuriousanimal.com/thebookofopenlayers3/chapter04_03_styling.html

    I am new to OpenLayers (and JavaScript) but the structure is logical and easy to replicate.

    enter image description here

    My full code here. You would just need to add IGC files and API key:

    <!DOCTYPE html>
    <html>
    <head>
    <title>IGC example</title>
    <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="http://openlayers.org/en/v3.10.1/css/ol.css" type="text/css">
    <script src="http://openlayers.org/en/v3.10.1/build/ol.js"></script>
    
    </head>
    <body>
    <div class="container-fluid">
    
    <div class="row-fluid">
      <div class="span12">
        <div id="map" class="map"></div>
        <input id="time" type="range" value="0" steps="1" />
        <div class="span4 offset4 pull-right">
          <div id="info" class="alert alert-success">
            &nbsp;
          </div>
        </div>
      </div>
    </div>
    
    </div>
    <script>
    
    
                // Geometries
                var point =  new ol.geom.Circle(ol.proj.transform([-96.1543889, 29.2542778], 'EPSG:4326', 'EPSG:3857'), 30000 );
                var circle = new ol.geom.Circle(ol.proj.transform([-96.1543889, 29.2542778], 'EPSG:4326', 'EPSG:3857'), 10000 );
    
                // Features
                var pointFeature = new ol.Feature(point);
                var circleFeature = new ol.Feature(circle);
    
                // Source and vector layer
                var vectorSource = new ol.source.Vector({
                    projection: 'EPSG:4326',
                    features: [pointFeature, circleFeature]
                });
    
                var style = new ol.style.Style({
                    fill: new ol.style.Fill({
                        color: 'rgba(20, 100, 240, 0.3)'
                    }),
                    stroke: new ol.style.Stroke({
                        width: 3,
                        color: 'rgba(0, 100, 240, 0.8)'
                    }),
                    image: new ol.style.Circle({
                        fill: new ol.style.Fill({
                            color: 'rgba(55, 200, 150, 0.5)'
                        }),
                        stroke: new ol.style.Stroke({
                            width: 10,
                            color: 'rgba(55, 200, 150, 0.8)'
                        }),
                        radius: 7
                    }),
                });
    
                var vectorLayer = new ol.layer.Vector({
                    source: vectorSource,
                    style: style
                });
    
    
    var colors = {
      'MH': 'rgba(0, 0, 255, 0.7)',
      'BW': 'rgba(0, 215, 255, 0.7)',
      'Sylvain Dhonneur': 'rgba(0, 165, 255, 0.7)',
      'Tom Payne': 'rgba(0, 255, 255, 0.7)',
      'Ulrich Prinz': 'rgba(0, 215, 255, 0.7)'
    };
    
    var styleCache = {};
    var styleFunction = function(feature, resolution) {
      var color = colors[feature.get('PLT')];
      var styleArray = styleCache[color];
      if (!styleArray) {
        styleArray = [new ol.style.Style({
          stroke: new ol.style.Stroke({
            color: color,
            width: 3
          })
        })];
        styleCache[color] = styleArray;
      }
      return styleArray;
    };
    
    var vectorSource = new ol.source.Vector();
    
    var igcUrls = [
      'BW.igc',
      'MH.igc',
      'data/igc/Sylvain-Dhonneur.igc',
      'data/igc/Tom-Payne.igc',
      'data/igc/Ulrich-Prinz.igc'
    ];
    
    function get(url, callback) {
      var client = new XMLHttpRequest();
      client.open('GET', url);
      client.onload = function() {
        callback(client.responseText);
      };
      client.send();
    }
    
    var igcFormat = new ol.format.IGC();
    for (var i = 0; i < igcUrls.length; ++i) {
      get(igcUrls[i], function(data) {
        var features = igcFormat.readFeatures(data,
            {featureProjection: 'EPSG:3857'});
        vectorSource.addFeatures(features);
      });
    }
    
    var time = {
      start: Infinity,
      stop: -Infinity,
      duration: 0
    };
    vectorSource.on('addfeature', function(event) {
      var geometry = event.feature.getGeometry();
      time.start = Math.min(time.start, geometry.getFirstCoordinate()[2]);
      time.stop = Math.max(time.stop, geometry.getLastCoordinate()[2]);
      time.duration = time.stop - time.start;
    });
    
    
    var wharton = [-96.1543889, 29.2542778]; // caution partner, read on...
    // since we are using OSM, we have to transform the coordinates...
    var whartonMercator = ol.proj.fromLonLat(wharton);
    
    
    
    
    var map = new ol.Map({
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM({
            attributions: [
              new ol.Attribution({
                html: 'All maps &copy; ' +
                    '<a href="http://www.opencyclemap.org/">OpenCycleMap</a>'
              }),
              ol.source.OSM.ATTRIBUTION
            ],
            url: 'https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png'
          })
        }),
        new ol.layer.Vector({
          source: vectorSource,
          style: styleFunction
        }),
        vectorLayer
      ],
      target: 'map',
      controls: ol.control.defaults({
        attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
          collapsible: true
        })
      }),
      view: new ol.View({
        center: whartonMercator,
        zoom: 9
      })
    });
    
    
    
    var point = null;
    var line = null;
    var displaySnap = function(coordinate) {
      var closestFeature = vectorSource.getClosestFeatureToCoordinate(coordinate);
      var info = document.getElementById('info');
      if (closestFeature === null) {
        point = null;
        line = null;
        info.innerHTML = '&nbsp;';
      } else {
        var geometry = closestFeature.getGeometry();
        var closestPoint = geometry.getClosestPoint(coordinate);
        if (point === null) {
          point = new ol.geom.Point(closestPoint);
        } else {
          point.setCoordinates(closestPoint);
        }
        var date = new Date(closestPoint[2] * 1000);
        info.innerHTML =
            closestFeature.get('PLT') + ' (' + date.toUTCString() + ')';
        var coordinates = [coordinate, [closestPoint[0], closestPoint[1]]];
        if (line === null) {
          line = new ol.geom.LineString(coordinates);
        } else {
          line.setCoordinates(coordinates);
        }
      }
      map.render();
    };
    
    map.on('pointermove', function(evt) {
      if (evt.dragging) {
        return;
      }
      var coordinate = map.getEventCoordinate(evt.originalEvent);
      displaySnap(coordinate);
    });
    
    map.on('click', function(evt) {
      displaySnap(evt.coordinate);
    });
    
    var imageStyle = new ol.style.Circle({
      radius: 5,
      fill: null,
      stroke: new ol.style.Stroke({
        color: 'rgba(255,0,0,0.9)',
        width: 1
      })
    });
    var strokeStyle = new ol.style.Stroke({
      color: 'rgba(255,0,0,0.9)',
      width: 1
    });
    map.on('postcompose', function(evt) {
      var vectorContext = evt.vectorContext;
      if (point !== null) {
        vectorContext.setImageStyle(imageStyle);
        vectorContext.drawPointGeometry(point);
      }
      if (line !== null) {
        vectorContext.setFillStrokeStyle(null, strokeStyle);
        vectorContext.drawLineStringGeometry(line);
      }
    });
    
    var featureOverlay = new ol.layer.Vector({
      source: new ol.source.Vector(),
      map: map,
      style: new ol.style.Style({
        image: new ol.style.Circle({
          radius: 5,
          fill: new ol.style.Fill({
            color: 'rgba(255,0,0,0.9)'
          }),
          stroke: null
        })
      })
    });
    
    document.getElementById('time').addEventListener('input', function() {
      var value = parseInt(this.value, 10) / 100;
      var m = time.start + (time.duration * value);
      vectorSource.forEachFeature(function(feature) {
        var geometry = /** @type {ol.geom.LineString} */ (feature.getGeometry());
        var coordinate = geometry.getCoordinateAtM(m, true);
        var highlight = feature.get('highlight');
        if (highlight === undefined) {
          highlight = new ol.Feature(new ol.geom.Point(coordinate));
          feature.set('highlight', highlight);
          featureOverlay.getSource().addFeature(highlight);
        } else {
          highlight.getGeometry().setCoordinates(coordinate);
        }
      });
      map.render();
    });
    
    </script>
    </body>
    </html>