0

I have a small dataset displayed as points in Leaflet. Each point has 3 arrows (right, left, head) coming out of it and they are rotated based on 3 separate fields (rhead, lhead, head). i want the labels to appear at the end of the arrows, so I'm starting to work on the left labels first. Eventually, both the left and right labels will be at the end of their respective arrows and pull data from their own fields. Here is what I have so far:

As you can see, the label is placed down and to the right of the points.

enter image description here

I want it to look like this so that the label respects the rotation of the left arrow (i've highlighted the left labels).

enter image description here

Here is my code:

// Add basemap 
    var map = L.map('map').setView([48.426, -123.367], 17); // Zoom to DT Victoria
    L.tileLayer('https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);

    //==============================================================
    // Define custom icons for each symbol (1 point, 3 arrows)
    //==============================================================
    // Sensor marker dimensions
    const circleRadius = 6; 
    // Arrow icon dimensions
    const arrowWidth = 64;
    const arrowHeight = 64;
    // Heading arrow icon dimensions
    const H_Height = 24;
    const H_Width = 24;

    // Left and right arrow SVG
    var arrow = L.icon({
        iconUrl: 'markers/LR.svg',
        iconSize: [arrowWidth, arrowHeight],
        iconAnchor: [arrowWidth / 2, arrowHeight + circleRadius] 
    });

    // Heading arrow SVG
    var H_arrow = L.icon({
        iconUrl: 'markers/head.svg',
        iconSize: [H_Width, H_Height],
        iconAnchor: [H_Width / 2, H_Height + circleRadius] 
    });

    //===============================================================
    // Function to place rotated symbols 
    //===============================================================
    // Function to calculate offset based on angle and length
        function placeRotatedSymbol(coords, lhead, rhead, head, label, feature) {
        console.log("Placing rotated symbol for feature:", feature);

        // Define calculateOffset function
        function calculateOffset(angle, length) {
            var offsetX = Math.cos((angle - 90) * Math.PI / 180) * length;
            var offsetY = Math.sin((angle - 90) * Math.PI / 180) * length;
            return [offsetX, offsetY];
        }

        // Calculate common offset for all arrow markers using the calculateOffset function
        var offset = calculateOffset(lhead, 0.00000000000009); // Adjust arrow offset as needed

        // Create left arrow marker
        var leftMarker = L.marker([coords[1] + offset[1], coords[0] + offset[0]], {
            icon: arrow,
            rotationAngle: lhead
        });
        leftMarker.addTo(map);
        console.log("Left arrow marker added to map:", leftMarker);

        // Create label marker
        var labelMarker = L.marker([coords[1] + offset[1], coords[0] + offset[0]], {
            icon: L.divIcon({
                className: 'arrow-label',
                html: label,
                iconAnchor: [0, 0],
            })
        });
        labelMarker.addTo(map);
        console.log("Label marker added to map:", labelMarker);

        // Create right arrow marker
        var rightMarker = L.marker([coords[1] + offset[1], coords[0] + offset[0]], {
            icon: arrow,
            rotationAngle: rhead
        });
        rightMarker.addTo(map);
        console.log("Right arrow marker added to map:", rightMarker);

        // Create heading arrow marker
        var headingMarker = L.marker([coords[1] + offset[1], coords[0] + offset[0]], {
            icon: H_arrow,
            rotationAngle: head
        });
        headingMarker.addTo(map);
        console.log("Heading arrow marker added to map:", headingMarker);

        return [leftMarker, labelMarker, rightMarker, headingMarker];
    }
    //======================================================
    // Loop through each feature in the data & place markers
    //======================================================
    data.features.forEach(function(feature) {   
        // Define variables to access fields (sensor coords, rotation angles for arrows)
        var coords = feature.geometry.coordinates; // Sensor coords stored as long lat (XY)
        var lhead = feature.properties.lhead; // left arrow angles stored in data (sensors.js)
        var rhead = feature.properties.rhead; // right arrow angles
        var head = feature.properties.head; // heading angles
        var label = feature.properties.L_ct; // label for left arrow

        // Call the function to place the rotated symbols on the map for each feature
        var markers = placeRotatedSymbol(coords, lhead, rhead, head, label, feature); 

        // Add circle marker for each point
        L.circleMarker([coords[1], coords[0]], { // Reverse the coords, leaflet takes lat long (YX)
            pane: 'tooltipPane',
            radius: circleRadius,
            color: '#FFFFFF',
            weight: 1,
            fill: true,
            fillColor: '#DBA044',
            fillOpacity: 1
        }).addTo(map);

        // Apply scale-dependent visibility 
        map.on('zoomend', function() {
            var zoomLevel = map.getZoom();
            if (zoomLevel >= 17) {
                markers.forEach(function(marker) {
                    map.addLayer(marker);
                });
            } else {
                markers.forEach(function(marker) {
                    map.removeLayer(marker);
                });
            }
        });
    });
2
  • 1
    Have a look at my comment to your question gis.stackexchange.com/questions/480182/…
    – TomazicM
    Commented Apr 19 at 7:16
  • Thanks again, I've updated the code here to match the code in the previous question that you helped me answer. Do you happen to have any tips for placing the labels at the end of each arrow? I was trying to do the left ones first but haven't had any success. Thanks for your patience, I'm very new to this Commented Apr 19 at 22:11

2 Answers 2

1

This is not really an answer yet, just comment with some code, since obviously you don't understand my comments to three of your questions.

There is absolutely no need to add any offset to the arrow markers. All the necessary offsets are taken care by icon anchor offsets. So your placeRotatedSymbol function should be simplifed to this:

function placeRotatedSymbol(coords, lhead, rhead, head, label, feature) {
  var leftMarker = L.marker([coords[1], coords[0]], {
    icon: arrow,
    rotationAngle: lhead
  });
  leftMarker.addTo(map);

  var labelMarker = L.marker([coords[1], coords[0]], {
    icon: L.divIcon({
      className: 'arrow-label',
      html: label,
      iconAnchor: [0, 0],
    })
  });
  labelMarker.addTo(map);

  var rightMarker = L.marker([coords[1], coords[0]], {
    icon: arrow,
    rotationAngle: rhead
  });
  rightMarker.addTo(map);

  var headingMarker = L.marker([coords[1], coords[0]], {
    icon: H_arrow,
    rotationAngle: head
  });
  headingMarker.addTo(map);

  return [leftMarker, labelMarker, rightMarker, headingMarker];
}

Once you correct this in your code, we can deal with labels.

1
  • Thank you and sorry for not understanding, I'm new to this. Your code is working perfectly for my map and it should be ready for offsetting the labels. Commented Apr 22 at 18:22
1

Labels can be created with rotated marker which has L.divIcon icon with desired text and icon anchor that puts them at the end of arrows. Since marker is rotated and so icon text is rotated with it, text has to be rotated back to horizontal position with style rotate transform.

Code for placing rotated symbol could then look something like this:

var labelOffset = 22;
var labelHeight = 18;
  
function labelMarker(coords, label, angle) {
  var labelElement = document.createElement('div');
  labelElement.innerHTML = label;
  labelElement.style.width = arrowWidth + 'px';
  labelElement.style.textAlign = 'center';
  labelElement.style.textAlign = labelHeight + 'px';
  labelElement.style.transformOrigin = 'center';
  var textAngle = (angle >= 0) ? angle : (angle + 360); 
  labelElement.style.transform = 'rotate(-' + textAngle + 'deg)';
  var iconAnchor = [arrowWidth / 2, arrowHeight + circleRadius + labelOffset];
  var marker = L.marker([coords[1], coords[0]], {
    icon: L.divIcon({
      className: 'arrow-label',
      html: labelElement,
      iconAnchor: iconAnchor,
    }),
    rotationAngle: angle
  });
  return(marker);
}

function placeRotatedSymbol(coords, lhead, rhead, head, leftLabel, rightLabel, feature) {
  var leftMarker = L.marker([coords[1], coords[0]], {
    icon: arrow,
    rotationAngle: lhead
  });
  leftMarker.addTo(map);

  var leftLabelMarker = labelMarker(coords, leftLabel, lhead);
  leftLabelMarker.addTo(map);

  var rightLabelMarker = labelMarker(coords, rightLabel, rhead);
  rightLabelMarker.addTo(map);

  var rightMarker = L.marker([coords[1], coords[0]], {
    icon: arrow,
    rotationAngle: rhead
  });
  rightMarker.addTo(map);

  var headingMarker = L.marker([coords[1], coords[0]], {
    icon: H_arrow,
    rotationAngle: head
  });
  headingMarker.addTo(map);

  return [leftMarker, leftLabelMarker, rightMarker, rightLabelMarker, headingMarker];
}

The following code:

L.circleMarker([48.426, -123.367], {
    pane: 'tooltipPane',
    radius: circleRadius,
    color: '#FFFFFF',
    weight: 1,
    fill: true,
    fillColor: '#DBA044',
    fillOpacity: 1
}).addTo(map);
   
placeRotatedSymbol([-123.367, 48.426], -50, 130, 40, '15', '7');

will then create the following symbol:

enter image description here

3
  • Big thank you to you again for help with the javascript....... I could not have done it without you. I really appreciate your patience and persistence with trying to assist me. Thanks for everything again! Commented Apr 22 at 19:59
  • 1
    I rolled back your question edit so that both answers make sense, so that also others besides you can make use of it.
    – TomazicM
    Commented Apr 22 at 20:21
  • Good idea, thank you again for your amazing help. You are a genius Commented Apr 22 at 20:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.