Листовка с диаграммой D3 - диаграмма должна оставаться на одном месте - PullRequest
5 голосов
/ 28 мая 2020

У меня есть листовка-карта с круговыми маркерами и радиальная гистограмма. Я хотел бы:

  1. круговые маркеры для перемещения с базовой картой (чтобы они оставались верными своему положению в реальном мире), но
  2. радиальная диаграмма оставалась постоянной в пределах окна / container при перемещении карты

Круговые маркеры перемещаются нормально, но радиальная диаграмма перемещается вместе с картой, что мне не нужно.

Я поместил круговые маркеры в central_map_svg, а радиальную диаграмму - в chart_svg. Оба они являются потомками leaflet_svg, и я думаю, что здесь возникает проблема. Однако, если у них нет одного и того же родителя, они отображаются отдельно.

Я включил упрощенный воспроизводимый код ниже.

enter image description here

<!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>

1 Ответ

4 голосов
/ 29 июня 2020

Простым решением было бы поставить контейнер поверх карты для размещения диаграммы.

Сначала измените HTML на

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

Затем добавьте стили для отображения #chart поверх #map

#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%;}

Если вы хотите, чтобы некоторые части диаграммы оставались интерактивными, добавьте правило для повторного включения событий указателя для этих элементов. Например:

#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>
...