Search code examples
javascripthtmlopenlayers

Adding markers to openlayers


I have a map where users should be able to click where they want and a marker will be added, when they add a new marker, a line will connect to the previous one creating a route. The route doesn't have to follow roads or anything, just a straight line connecting the points.

So far I have this code where the idea would be that users click on the map, a marker appears and it is stored so that it can be removed later or so that the route can be saved to load it later:

const markers = [];
const view = new View({
    projection: 'EPSG:4326',
    center: [-3.704322881508588, 40.4074397621576],
    zoom: 12,
    extent: [-9.844, 35.747, 4.878, 43.993],
  });

let map = new Map({
    layers: [
      new TileLayer({
        source: new OSM(),
      }),
    ],
    keyboardEventTarget: document,
    target: 'mapa',
    view: view,
    controls: defaultControls().extend([new ZoomSlider()]),
});
map.on('click', function(evt){
  console.info(evt.pixel);
  console.info(map.getPixelFromCoordinate(evt.coordinate));
  console.info(evt.coordinate);
  var coords = evt.coordinate;
  var lat = coords[1];
  var lon = coords[0];
  var locTxt = "Latitude: " + lat + " Longitude: " + lon;
  
  let marker = new ol.Feature({
    geometry: new ol.geom.Point(
        ol.proj.fromLonLat([lon, lat])
    ),
  });
  
  marker.setStyle(new ol.style.Style({
    image: new ol.style.Icon({
        src: "/imgs/chincheta.png",
    })
  }));
  markers.push(marker);
  let capa = new ol.layer.Vector({
      source: new ol.source.Vector({
          features: markers, 
      }),
  });
  
  map.addLayer(capa);
});

The HTML just has a div "mapa" and the reference to this script like this: <script type="module" src="js/mapa.mjs"></script>. The problem is that it does not let me add any marker, if I change markers with marker in the line features: markers, it throws an error: i.getArray is not a function so I don't know what's wrong here.


Solution

  • This is the solution I ended up with:

    let draw;
    let dibujo;
    let coordinates = [];
    let old_coordinates = [];
    let map;
    
    // Layer for drawing waypoints.
    const drawVector = new ol.layer.Vector({
        source: new ol.source.Vector({ wrapX: false }),
        style: function (feature) {
            return style[feature.get("type")] || style.line;
        },
    });
    
    // Style for the markers and the lines connecting them
    const style = {
        // Waypoints style
        marker: new ol.style.Style({
            image: new ol.style.Icon({
                anchor: [0.5, 1],
                src: "imgs/marker.png",
            }),
        }),
        // Line style
        line: new ol.style.Style({
            stroke: new ol.style.Stroke({
                color: 'blue',
                width: 2
            })
        })
    };
    
    function init() {
        // Layer that shows a map made from raster tiles from the server.
        const raster = new ol.layer.Tile({
            source: new ol.source.XYZ({
                url: 'imgs/sat_tiles/{z}/{x}/{y}.png' // Ruta donde se encuentran los archivos tiles descargados
            }),
        });
        // This layer restricts the area where you are allowed to draw
        var limite = new ol.layer.Vector({
            source: new ol.source.Vector(),
            style: new ol.style.Style({
                stroke: new ol.style.Stroke({
                    color: "red",
                    width: 2,
                    opacity: 1,
                }),
            }),
        });
        // This Feature draws the restricted area using the points from limites which is an array formed by the waypoints that limits the area, you need at least three.
        var limiteFeature = new ol.Feature({
            geometry: new ol.geom.Polygon([limites]),
        });
        limite.getSource().addFeature(limiteFeature);
        // Layer for drawing on the map.
        draw = new ol.interaction.Draw({
            source: drawVector.getSource(),
            type: 'LineString',
            freehandCondition: ol.events.condition.never,
            finishCondition: ol.events.condition.never,
        });
        // Map with all the layers created plus another one for "static waypoints" that the user cannot change.
        map = new ol.Map({
            layers: [raster, limite, staticVector, drawVector],
            keyboardEventTarget: document,
            target: "mapa",
            //Centramos la vista en el aeropuerto y limitamos la vista a España
            view: new ol.View({
                projection: "EPSG:4326",
                // This would determine the center point at the start and the maximum area visible at minimum zoom (censored just in case).
                center: [XX.XXXXX, XX.XXXXX],
                extent: [XX.XXXXX, XX.XXXXX, XX.XXXXX, XX.XXXXX],
                zoom: 16
            }),
        });
        // And here is what happens when you click on the map to add waypoints
        map.on("click", function (evt) {
            // I have a drawing mode so that you do not add waypoints by accident, just when you have clicked the option to add them.
            if (dibujo == true) {
                //Check restricted area
                var esIncluido = limiteFeature.getGeometry().intersectsCoordinate(evt.coordinate); 
                if (esIncluido) {
                    // If the point is create the marker.
                    point = new ol.geom.Point(evt.coordinate);
                    marker = new ol.Feature({
                        type: "marker",
                        geometry: point,
                    });
                    // Save the point
                    coordinates.push(marker);
                    // Clear the array used for redoing deleted points.
                    old_coordinates = [];
                    // Place marker on the map
                    drawVector.getSource().addFeature(marker);
                } else {
                    // The point is added even when clicking outside of the allowed area so if it was not allowed we remove it.
                    draw.removeLastPoint();
                }
            }
        });
    }
    // Enter the waypoints or drawing mode:
    function waypoints() {
        dibujo = true;
        map.addInteraction(draw)
        // Delete what was drawn previosly
        drawVector.getSource().clear();
        drawVector.getSource().clear();
        coordinates = [];
        old_coordinates = [];
    }
    // Clear the drawing
    function waypointsClear() {
        draw.abortDrawing();
        drawVector.getSource().clear();
        coordinates = [];
        old_coordinates = [];
        dibujo = true;
        save = false;
        map.addInteraction(draw)
    }
    // Remove the last waypoint.
    function waypointsUndo() {
        if (save == false) {
            draw.removeLastPoint();
            const lastCoord = coordinates.pop();
            drawVector.getSource().removeFeature(lastCoord);
            old_coordinates.push(lastCoord);
            drawVector.getSource().changed();
        }
    }
    // Redo the last undoed waypoint
    function waypointsRedo() {
        if (old_coordinates.length > 0 && save == false) {
            let last = old_coordinates.pop();
            draw.appendCoordinates([last.getGeometry().getCoordinates()]);
            drawVector.getSource().addFeature(last);
            coordinates.push(last);
        }
    }
    

    This code allows to place markers and connect them with a line and doing some other things with them.