Positioning a tooltip above a rectangular SVG path

Recently I was working with a shapefile dividing the state of MA into rectangular grids. I wanted to position the tooltip relative to a rectangluar svg path on a Leaflet map overlay. Usually when I want to provide info on a path it is a data line and I place the tooltip based on the mouse coordinates, as discussed¬†in my¬†previous post about tooltips. In this case, I wanted to place it centered over the data, where the highlighted region below is an SVG “path”, and not a “rect” element:

arlingtonTooltip

It turns out the path description is pretty simple for a rectangle. In geoJSON the rectangle geometry element is one shape (geometry.coordinates[0]). The rectangle is encoded with 5 data points, where the first and last are the same, as show in the screen capture below:

geojsonRect

The GeoJSON coordinates here are WGS84.

Inspecting the DOM for one of these paths after rendering shows that the SVG path has only the required 4 data points and is closed by the “Z”. Here the values are SVG coordinates.

<path d="M293,160L222,160L222,89L293,89Z"></path>

I wrote the following function to calculate the top center position of the rectangle, for the purpose of positioning the tooltip.

// locates the top center SVG coordinates for a rectangular GeoJSON feature
function getRectangularPathTopCenterPosition(d){
    // Use 1st and 3rd data point to get bounds of rectangle
    pointA = d.geometry.coordinates[0][0];
    pointB = d.geometry.coordinates[0][2];

    // convert from coordinate system to SVG coordinates
    var point1 = map.latLngToLayerPoint(
        new L.LatLng(pointA[1], pointA[0])); 
    var point2 = map.latLngToLayerPoint(
        new L.LatLng(pointB[1], pointB[0])); 

    // get middle x value and min y value
    var x_pos = (point1.x + point2.x)/2;
    var y_pos = Math.min(point1.y, point2.y);

    // get the offset of the map on the page, 
    // using the overlay we are drawing on
    var mapOffsets = $(map.getPanes().overlayPane).offset();

    return {"x": x_pos + mapOffsets.left, 
            "y": y_pos + mapOffsets.top};
}

In the end I didn’t like the tooltip moving all over the screen and obstructing the map, so I used a statically positioned data table that updated on mouseover. Even though I didn’t use it, the approach above was so much better than positioning based on the mouse and obstructing the region of interest that I thought I’d share it anyway.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>