emilhe / dash-leaflet

MIT License
204 stars 33 forks source link

Leaflet.Path.Transform #246

Open pip-install-python opened 2 weeks ago

pip-install-python commented 2 weeks ago

DashDiscord-ezgif com-crop

Thought this was interesting: https://milevski.co/Leaflet.Path.Transform/

was able to set it up in dash component boilerplate and thought it could be a useful addition to dash leaflet. The idea I've started to come up with is to expand the functionality of the component to extend further with drag, rotate and resize. Not entirely sure how it would all be setup but wanted to share what I was able to get setup in my react.js file:

import React, { useEffect, useRef } from 'react';
import L from 'leaflet';
import 'leaflet-path-drag';
import 'leaflet-path-transform';
import 'leaflet/dist/leaflet.css';

const dlpt = ({ id}) => {
    const mapRef = useRef(null);
    const leafletMap = useRef(null);

    useEffect(() => {
    if (!leafletMap.current) {
        leafletMap.current = L.map(mapRef.current, {
            // eslint-disable-next-line no-magic-numbers
            center: [51.505, -0.09],
            zoom: 13
        });

        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 19,
            attribution: '© OpenStreetMap'
        }).addTo(leafletMap.current);

        const polygon = L.polygon([
            // eslint-disable-next-line no-magic-numbers
            [51.509, -0.08],
            // eslint-disable-next-line no-magic-numbers
            [51.503, -0.06],
            // eslint-disable-next-line no-magic-numbers
            [51.51, -0.047]
        ], { transform: true, draggable: true }).addTo(leafletMap.current);

        // Enable transformations
        polygon.transform.enable({
            rotation: true,
            scaling: true
        });

        // Enable dragging
        polygon.dragging.enable();
    }
}, []);

    return <div id={id} ref={mapRef} style={{ height: '100vh', width: '100vw' }}></div>;
};

export default dlpt;

Ideally It would be awesome if it could extend to images as they are difficult to position as overlays on the map, this would allow the user a way ideally to add an image, resize it and rotate it over the map to get that perfect fit. I wasn't able to get the rotation to work but was able to get the resize and drag to work with:

const dlpt = ({ id }) => {
    const mapRef = useRef(null);
    const leafletMap = useRef(null);

    useEffect(() => {
        if (!leafletMap.current) {
            leafletMap.current = L.map(mapRef.current, {
                // eslint-disable-next-line no-magic-numbers
                center: [51.505, -0.09],
                zoom: 13
            });

            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                maxZoom: 19,
                attribution: '© OpenStreetMap'
            }).addTo(leafletMap.current);

            const imageUrl = 'https://store-images.s-microsoft.com/image/apps.36868.bfb0e2ee-be9e-4c73-807f-e0a7b805b1be.712aff5d-5800-47e0-97be-58d17ada3fb8.a46845e6-ce94-44cf-892b-54637c6fcf06';
            const centerLat = 51.5095;
            const centerLng = -0.0635;
            const latHeight = 0.001;
            // eslint-disable-next-line no-magic-numbers
            const lngWidth = latHeight * (800 / 600);

            const imageBounds = [
                [centerLat - latHeight / 2, centerLng - lngWidth / 2],
                [centerLat + latHeight / 2, centerLng + lngWidth / 2]
            ];

            const image = L.imageOverlay(imageUrl, imageBounds, {
                opacity: 0.7,
                interactive: true
            }).addTo(leafletMap.current);

            const polygon = L.polygon([
                [centerLat - latHeight / 2, centerLng - lngWidth / 2],
                [centerLat - latHeight / 2, centerLng + lngWidth / 2],
                [centerLat + latHeight / 2, centerLng + lngWidth / 2],
                [centerLat + latHeight / 2, centerLng - lngWidth / 2]
            ], {
                color: 'transparent',
                transform: true,
                draggable: true
            }).addTo(leafletMap.current);

            polygon.transform.enable({
                rotation: true,
                scaling: true
            });

            polygon.on('dragend transform', function (e) {
                const newBounds = polygon.getBounds();
                image.setBounds(newBounds);

                if (e.rotation) {
                    // eslint-disable-next-line no-magic-numbers
                    const rotationDegrees = e.rotation * (180 / Math.PI);
                    const imageElement = image.getElement();
                    imageElement.style.transformOrigin = 'center center';
                    imageElement.style.transform = `rotate(${rotationDegrees}deg)`;
                }
            });
        }
    }, []);

    return <div id={id} ref={mapRef} style={{ height: '100vh', width: '100vw' }}></div>;
};

export default dlpt;

Outside of maping this could also expand into an interesting concept of being able to create the same functionality of the mac app Freeform if built upon. Just exploratory and figured it was worth sharing.

cheers, Pip

emilhe commented 2 days ago

This is an interesting concept. One way to integrate it would be to add properties to the existing svg components that would enable this behavior.