【问题标题】:Leaflet overlaid with D3 Chart - Need chart to remain in one place用 D3 图表覆盖的传单 - 需要将图表保留在一个地方
【发布时间】:2020-09-15 20:09:19
【问题描述】:

我有一张带有圆形标记和径向条形图的传单地图。我愿意:

  1. 与基础地图一起移动的圆形标记(以便它们保持 忠实于他们在现实世界中的立场)但是
  2. 径向图表在窗口/容器内保持不变 当地图移动时

圆形标记移动正常,但径向图表随着我不想要的地图移动。

我已将圆形标记放置在 central_map_svg 中,并将径向图表放置在 chart_svg 中。两者都是leaflet_svg的孩子,我认为这是问题出现的地方。但是,如果它们没有相同的父级,则它们会单独出现。

我在下面包含了简化的可重现代码。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://d19vzq90twjlae.cloudfront.net/leaflet-0.7/leaflet.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
    <!-- Load d3.js -->
    <script src="https://d3js.org/d3.v5.js"></script>
    <!-- Function for radial charts -->
    <script src="https://cdn.jsdelivr.net/gh/holtzy/D3-graph-gallery@master/LIB/d3-scale-radial.js"></script>
    <!-- Leaflet -->
    <script src="https://d19vzq90twjlae.cloudfront.net/leaflet-0.7/leaflet.js"></script>

</head>
<body>
    <div id="map" style="width: 800px; height: 800px"></div>
    <script type="text/javascript">

        // set the dimensions and margins of the graph
        var size = 800;
        var margin = { top: 100, right: 0, bottom: 0, left: 0 },
            width = size - margin.left - margin.right,
            height = size - margin.top - margin.bottom,
            innerRadius = 240,
            outerRadius = Math.min(width, height) / 2;
        var mapCenter = new L.LatLng(52.482672, -1.897517);
        var places = [
            {
                "id": 1,
                "value": 15,
                "latitude": 52.481,
                "longitude": -1.899
            },
            {
                "id": 2,
                "value": 50,
                "latitude": 52.486,
                "longitude": -1.897
            },
            {
                "id": 3,
                "value": 36,
                "latitude": 52.477,
                "longitude": -1.902
            },
            {
                "id": 4,
                "value": 65,
                "latitude": 52.486,
                "longitude": -1.894
            }]

        var map = L.map('map').setView(mapCenter, 15);
        mapLink =
            '<a href="http://openstreetmap.org">OpenStreetMap</a>';
        L.tileLayer(
            'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; ' + mapLink + ' Contributors',
            maxZoom: 18
        }).addTo(map);

        // Disable mouse zoom as this causes drift
        map.scrollWheelZoom.disable();
        // Initialize the SVG layer
        map._initPathRoot();


        /* We simply pick up the SVG from the map object */
        var leaflet_svg = d3.select("#map").select("svg"),
            central_map_svg = leaflet_svg.append("g"),
            chart_svg = leaflet_svg.append("g")
                .attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");

        function plot(data) {
            /* Add a LatLng object to each item in the dataset */
            data.forEach(function (d) {
                d.LatLng = new L.LatLng(d.latitude, d.longitude)
            })

            // X scale
            var x = d3.scaleBand()
                .range([0, 2 * Math.PI])
                .domain(data.map(function (d) { return d.id; }));

            // Y scale
            var y = d3.scaleRadial()
                .range([innerRadius, outerRadius])
                .domain([0, 68]); 

            // Add the bars
            chart_svg.append("g")
                .selectAll("path")
                .data(data)
                .enter()
                .append("path")
                .attr("d", d3.arc()
                    .innerRadius(y(0))
                    .outerRadius(function (d) { return y(d.value); })
                    .startAngle(function (d) { return x(d.id); })
                    .endAngle(function (d) { return x(d.id) + x.bandwidth(); })
                    .padAngle(0.02)
                    .padRadius(innerRadius))

            // Add the circles
            var feature = central_map_svg
                .selectAll("circle")
                .data(data)
                .enter().append("circle")
                .attr("r", 15)

            // Ensure circles correctly positoned after map zoom / update
            map.on("viewreset", update);
            update();

            function update() {
                feature.attr("transform",
                    function (d) {
                        return "translate(" +
                            map.latLngToLayerPoint(d.LatLng).x + "," +
                            map.latLngToLayerPoint(d.LatLng).y + ")";
                    }
                );
            }
        }
        plot(places)
    </script>
</body>

【问题讨论】:

    标签: javascript css d3.js leaflet


    【解决方案1】:

    一个简单的解决方案是在地图顶部堆叠一个容器以容纳您的图表。

    首先,将您的 HTML 更改为

    <div id='scene'>
        <div id="map"></div>
        <div id='chart'></div>
    </div>
    

    然后添加样式以在#map之上显示#chart

    #scene {width: 800px; height: 800px; position: relative;}
    #map {width: 100%; height: 100%; z-index: 1;}
    #chart {width: 100%; height: 100%; position: absolute; z-index: 2; top:0; left: 0; pointer-events: none;}
    #chart svg {width: 100%; height: 100%;}
    

    如果您想让图表的某些部分保持交互,请添加一条规则以在这些元素上重新启用 pointer events。例如:

    #chart path {pointer-events: auto;}
    

    最后,将chart_svg 指向正确的元素:

    chart_svg = d3.select("#chart").append("svg").append("g")
    

    还有一个演示

    // set the dimensions and margins of the graph
    var size = 400;
    var margin = { top: 50, right: 0, bottom: 0, left: 0 },
        width = size - margin.left - margin.right,
        height = size - margin.top - margin.bottom,
        innerRadius = 120,
        outerRadius = Math.min(width, height) / 2;
    var mapCenter = new L.LatLng(52.482672, -1.897517);
    var places = [
        {
            "id": 1,
            "value": 15,
            "latitude": 52.481,
            "longitude": -1.899
        },
        {
            "id": 2,
            "value": 50,
            "latitude": 52.486,
            "longitude": -1.897
        },
        {
            "id": 3,
            "value": 36,
            "latitude": 52.477,
            "longitude": -1.902
        },
        {
            "id": 4,
            "value": 65,
            "latitude": 52.486,
            "longitude": -1.894
        }]
    
    var map = L.map('map').setView(mapCenter, 15);
    mapLink =
        '<a href="http://openstreetmap.org">OpenStreetMap</a>';
    L.tileLayer(
        'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; ' + mapLink + ' Contributors',
        maxZoom: 18
    }).addTo(map);
    
    // Disable mouse zoom as this causes drift
    map.scrollWheelZoom.disable();
    // Initialize the SVG layer
    map._initPathRoot();
    
    
    /* We simply pick up the SVG from the map object */
    var leaflet_svg = d3.select("#map").select("svg"),
        central_map_svg = leaflet_svg.append("g"),
        chart_svg = d3.select("#chart").append("svg").append("g")
            .attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");
    
    function plot(data) {
        /* Add a LatLng object to each item in the dataset */
        data.forEach(function (d) {
            d.LatLng = new L.LatLng(d.latitude, d.longitude)
        })
    
        // X scale
        var x = d3.scaleBand()
            .range([0, 2 * Math.PI])
            .domain(data.map(function (d) { return d.id; }));
    
        // Y scale
        var y = d3.scaleRadial()
            .range([innerRadius, outerRadius])
            .domain([0, 68]);
    
        // Add the bars
        chart_svg.append("g")
            .selectAll("path")
            .data(data)
            .enter()
            .append("path")
            .attr("d", d3.arc()
                .innerRadius(y(0))
                .outerRadius(function (d) { return y(d.value); })
                .startAngle(function (d) { return x(d.id); })
                .endAngle(function (d) { return x(d.id) + x.bandwidth(); })
                .padAngle(0.02)
                .padRadius(innerRadius))
                
        chart_svg
            .selectAll("path")
            .on("mouseover", function() {
                d3.select(this).style("fill", "red");
            })
            .on("mouseout", function() {
                d3.select(this).style("fill", "black");
            })
            .on("touchend", function() {
                var el = d3.select(this);
                el.style("fill", el.style("fill") === "red" ? "black" : "red");
            })
        ;
        // Add the circles
        var feature = central_map_svg
            .selectAll("circle")
            .data(data)
            .enter().append("circle")
            .attr("r", 15)
    
        // Ensure circles correctly positoned after map zoom / update
        map.on("viewreset", update);
        update();
    
        function update() {
            feature.attr("transform",
                function (d) {
                    return "translate(" +
                        map.latLngToLayerPoint(d.LatLng).x + "," +
                        map.latLngToLayerPoint(d.LatLng).y + ")";
                }
            );
        }
    }
    plot(places)
    #scene {width: 400px; height: 400px; position: relative;}
    #map {width: 100%; height: 100%; z-index: 1;}
    #chart {width: 100%; height: 100%; position: absolute; z-index: 2; top:0; left: 0;  pointer-events: none;}
    #chart svg {width: 100%; height: 100%;}
    #chart path {pointer-events: auto;}
    <link rel="stylesheet" href="https://d19vzq90twjlae.cloudfront.net/leaflet-0.7/leaflet.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
    <!-- Load d3.js -->
    <script src="https://d3js.org/d3.v5.js"></script>
    <!-- Function for radial charts -->
    <script src="https://cdn.jsdelivr.net/gh/holtzy/D3-graph-gallery@master/LIB/d3-scale-radial.js"></script>
    <!-- Leaflet -->
    <script src="https://d19vzq90twjlae.cloudfront.net/leaflet-0.7/leaflet.js"></script>
    
    
    <div id='scene'>
        <div id="map"></div>
        <div id='chart'></div>
    </div>

    【讨论】:

    • 非常感谢@nikoshr,这是完美的,正是我所追求的。感谢您抽出宝贵时间。
    • 嗨@nikoshr。我有另一个问题。我已将我的最新更改应用于您建议的代码,包括使用 Leaflet V1.6 和使用 d3 -tip 添加工具提示。当您将鼠标悬停在圆形或条形上时,工具提示应该会出现,并且相关的条形和圆形会以笔划突出显示。
    • 如果我将#chart pointer-events 保留为无,则将鼠标悬停在圆圈上会起作用,工具提示会显示并且白色边框会显示/扩展在相关的条形和圆圈上,但如果我将鼠标悬停在吧,什么都没有触发。如果我将其更改为指针事件:可见,则悬停在栏上会触发圆圈上的笔划以突出显示,但不会触发栏。我已经更新了这个 jsfiddle jsfiddle.net/colourblue/b2843mkr/28 中的代码
    • @Chris 您可以在 SVG 的特定部分重新启用指针事件。我已经修改了我的答案来举个例子,这是一个更新的 Fiddle jsfiddle.net/7a21epvd
    • 非常感谢@nikoshr,你真的帮了大忙。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多