d3.js 单击并应用缩放和平移将位于目标分区内的点分布到触发的分割

标签 d3.js svg zooming map-projections

基于 the response and example made by Andrew Reid ,我制作了这个
笔代码在这里points_in_subdivisons :点击屏幕上的区域(德国)
我们希望提供从 map 上的一个特写镜头到另一个特写镜头的流畅动画
通过使用缩小、平移、放大。

  • 我在国家一级有许多部门(国家),然后在每个国家内部有许多子部门(地区)。
  • 在我的例子中,许多点分散在所有部门(国家)中,主要是在德国之上。
  • 当我必须点击目标部门(国家)时,我必须只获得与我刚刚点击的目标部门(国家)相对应的点
  • 这意味着当分割(区域)的缩放被触发时(当点击时
    制作),
    代码应该采用所有已经存在的点
    目标分区(国家)的轮廓(刚刚被点击)和点
    封闭的应该分散在它们相应的分割(区域)中。

  • 为了实现这个功能和
    基于迈克尔·罗文斯基的评论:
    在函数操纵()中,代码能够仅过滤和提取嵌入在目标和触发分割(区域)内部的点,并排除那些外部的标记。
    在函数 redraw() 中,输入退出模式运行良好。
    var svg = d3.select("svg");
    
    width = 960;
    height = 500;
    var dataArray  = [];
    var mydataArray= []; 
    var projection = d3.geoMercator();
    var baseProjection = d3.geoMercator();
    
    var path = d3.geoPath().projection(projection);
    var gBackground = svg.append("g"); // appended first
    var  gProvince = svg.append("g");  
    var gDataPoints = svg.append("g"); // appended second
    var ttooltip = d3.select("body").append("div")
          .attr("class", "ttooltip");
    var csvPath="https://dl.dropbox.com/s/rb9trt4zy87ezi3/lonlat.csv?dl=0";
    
    d3.csv(csvPath, function(error, data) {
    
      if (error) throw error;
    
      d3.json("https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts0.json", function(error, nuts0) {
    
      if (error) throw error;
    
      d3.json("https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts2.json", function(error, nuts2) {
        if (error) throw error;
    
      // convert topojson back to geojson
      var countries = topojson.feature(nuts0, nuts0.objects.nuts0);
      var regions = topojson.feature(nuts2, nuts2.objects.nuts2);
      baseProjection.fitSize([width,height],regions);
      projection.fitSize([width,height],regions);
      var color = d3.scaleLinear().range(["steelblue","darkblue"]).domain([0,countries.features.length]);
      var regionColor = d3.scaleLinear().range(["orange","red"]);
    
      baseProjection.fitSize([width,height],countries);
      projection.fitSize([width,height],countries);
      var featureCollectionCountries = { "type":"FeatureCollection", "features": countries.features };
      gBackground
        .attr("class", "country")
        .selectAll("path")
        .data(countries.features)
        .enter()
        .append("path")
        .attr("fill",function(d,i) { return color(i); })
        .attr("opacity",0.7)
        .attr("d", path)
        .style("stroke","black")
        .style("stroke-width",0)
        .on("mouseover", function() {
            d3.select(this)
              .style("stroke-width",1)
              .raise();
        })
        .on("mouseout", function(d,i) {
            d3.select(this)
              .style("stroke-width", 0 );
        })
        ///// now zoom in when clicked and show subdivisions:
        .on("click", function(d) {
            // remove all other subdivisions:
            d3.selectAll(".region")
              .remove();
    
            // add new features:
    
    
            var features = regions.features.filter(function(feature) { return feature.properties.nuts_id.substring(0,2) == d.properties.nuts_id; });
    
    
    
            regionColor.domain([0,features.length])
    
            gProvince.selectAll(null)
              .data(features)
              .enter()
              .append("path")
              .attr("class","region")
              .attr("fill", function(d,i) { return regionColor(i) })
              .attr("d", path)
              .style("stroke","black")
              .style("stroke-width",0)
              .on("click", function() {
                zoom(projection,baseProjection);
                d3.selectAll(".subdivision")
                  .remove();          
              })
              .on("mouseover", function() {
                    d3.select(this)
                      .style("stroke-width",1)
                      .raise();
              })
              .on("mouseout", function(d,i) {
                    d3.select(this)
                      .style("stroke-width", 0 );
              })
              .raise()
    
            // zoom to selected features:
            var featureCollection = { "type":"FeatureCollection", "features": features }
    
            manipulate(data,features);
            redraw(featureCollection);
    
            var endProjection = d3.geoMercator();
    
            zoom(projection,endProjection.fitExtent([[50,50],[width-50,height-50]],featureCollection));
    
        }); 
    
        dataArray  = data;
        redraw(featureCollectionCountries);
      });
    });
    });
    
    function zoom(startProjection,endProjection,middleProjection) {
    
      if(!middleProjection) {
          d3.selectAll("path")
            .transition()
            .attrTween("d", function(d) {
              var s = d3.interpolate(startProjection.scale(), endProjection.scale());
              var x = d3.interpolate(startProjection.translate()[0], endProjection.translate()[0]);
              var y = d3.interpolate(startProjection.translate()[1], endProjection.translate()[1]);
                return function(t) {
                  projection
                    .scale(s(t))
                    .translate([x(t),y(t)])
    
                  path.projection(projection);
                  return path(d);
                }
             })
            .duration(1000);
        }
        else {
          d3.selectAll("path")
            .transition()
            .attrTween("d", function(d) {
    
              var s1 = d3.interpolate(startProjection.scale(),middleProjection.scale());
              var s2 = d3.interpolate(middleProjection.scale(),endProjection.scale()); 
              var x = d3.interpolate(startProjection.translate()[0], endProjection.translate()[0]);
              var y = d3.interpolate(startProjection.translate()[1], endProjection.translate()[1]);
    
              function s(t) {
                if (t < 0.5) return s1; return s2; 
              }
    
               return function(t) {                 
                   projection
                    .translate([x(t),y(t)])
                    .scale(s(t)(t))
    
                  path.projection(projection);
                  return path(d);
                }
             })
            .duration(1500);    
        }
    
    }
    
    
    function redraw(featureCollection,type) {
            var mapG = d3.select('svg g.country');
    
            d3.selectAll('circle')
              .remove();
    
            let grp = gDataPoints
                      .attr("class", "circle")
                      .selectAll("circle")
                      .data(dataArray,function(d) { return d.NOM; })
            let grpEnter = grp.enter()
            let group = grpEnter
            group.append("circle")
                 .attr('fill', 'rgba(135, 5, 151, 125)')
                 .attr('stroke', 'black')
                 .each(function(d) {
                                     if (d.lon === null ) return;
                                     if (isNaN(d.lon ))return;
                                     if (d.lat === null) return;
                                     if (isNaN(d.lat ))return;
                                     var pos = projection([parseFloat(d.lon), parseFloat(d.lat)]);
                                     d.cx = pos[0];
                                     d.cy = pos[1];
                 })
                 .attr("cx", function(d) {
                            return d.cx;
                 })
                .attr("cy", function(d) {
                        return d.cy;
                })
                .attr("r",0.5)
                .on("mouseover", showTooltip)
                .on("mouseout", hideTooltip)          
                .on('mousemove', function(d) {
                                    var xPos = d3.mouse(this)[0] - 15;
                                    var yPos = d3.mouse(this)[1] - 55;
                                    ttooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
                                    ttooltip.style('opacity', 1);
                                    var html = "<span>" + d.lon+ "</span>, <span>" + d.lat + "</span>";
                                     ttooltip.html(html);
    
                });
                // Setup each circle with a transition, each transition working on transform attribute,
                // and using the translateFn
                group
                    .transition()
                    .duration(2000)
                    .attrTween("transform",function(d) {
                                 return  mapG._groups[0][0] != null ? recenter(featureCollection): null;
                     });
                group.exit().remove() // exit > remove >  g
    
        }
    
    
    
        function recenter(featureCollection) {
            console.log('recentering');     
    
        };
    
    
    function manipulate(data,features){      
    
                        dataArray= [];
                        mydataArray =[];        
    
                        data.forEach(function(ddd) 
                        {
                            features.forEach(function(feature) 
                            {
                                var polygoneOriginal =feature;
    
                                var points = [parseFloat(ddd.lon), parseFloat(ddd.lat)];
    
                                var isIn = d3.geoContains(polygoneOriginal, points);
                                if(isIn)
                                {
    
                                   var element = ddd;
                                    mydataArray.pushIfNotExist(element, function(e) { 
                                        return e.lat === element.lat && e.lon === element.lon   ; 
                                    });
    
                                }
    
                            });
    
    
                        });
    
                        if(mydataArray.length>0)
                        {
    
                           var columnsArray= ["lon","lat"];
                           dataArray=mydataArray;
                           dataArray.columns = columnsArray;
    
    
                        }      
    }
    
    
        function showTooltip(d) {
          var html = "<span>" + d.lon+ "</span>, <span>" + d.lat + "</span>";
          ttooltip.html(html);
          ttooltip
            .style("left", window.pageXOffset + d3.event.x + 12 + "px")
            .style("top", window.pageYOffset + d3.event.y + 12 + "px")
            .transition()
            .style("opacity", 1);
    
            return d3.select(this).attr('fill', 'rgba(103, 65, 114, 0.8)');
    
        }
    
    function hideTooltip() {
          ttooltip
            .transition()
            .style("opacity", 0);
             return d3.select(this).attr('fill', 'rgba(103, 65, 114, 0.5)');
    }
    
    
    // check if an element exists in array using a comparer function
    // comparer : function(currentElement)
    Array.prototype.inArray = function(comparer) { 
        for(var i=0; i < this.length; i++) { 
            if(comparer(this[i])) return true; 
        }
        return false; 
    }; 
    
    // adds an element to the array if it does not already exist using a comparer 
    // function
    Array.prototype.pushIfNotExist = function(element, comparer) { 
        if (!this.inArray(comparer)) {
            this.push(element);
        }
    };
    

    我的问题如下:如何使缩放(对于点圈)充分工作:
    现在,在 map 上单击 x y 点未按比例缩放。
    它们在背景中呈现为圆圈,我希望它们随 map 移动。
    这意味着如何应用相同的动画缩放(当通过单击分区触发分割时),以便目标分割内的那些点跟随转换并随 map 移动,我们可以看到清晰分布的圆形点 在每个正确的相应分割中充分地 ?

    更新

    Andrew Reid 描述 here如何使用 d3.js 实现平滑缩放

    所以按照他的提示。

    我在 redraw() 函数中添加了以下说明
                var mapG = d3.select('svg g.country');
                 group
                .transition()
                .duration(2000)
                .attrTween("transform",function(d) {
                             return  mapG._groups[0][0] != null ? recenter(): null;
                 });
    

    然后我们应该将代码添加到实际上应该执行移动recenter(featureCollection)函数的函数中
               function recenter(featureCollection) {
               // TO ADD CODE TO BE IMPLEMENTED HERE 
               };
    

    非常感谢您的合作、参与和帮助!

    最佳答案

    1- 要生成第一次迭代,请单击 Region equal country

    //GENERATE FIRST MAP 
            dataArray  = data;
            redraw(); 
    

    2- 要生成县,例如单击区域,我们应该首先在缩放功能中设置 startprojection 和 endprojection,然后触发重绘圆圈
      //zoom to selected provinces features:
       var countiesFeatureCollection = { "type":"FeatureCollection", "features": countiesFeatures }
      //manipulate counties And Redraw
       manipulateCounties(data,countiesFeatures);
       baseProjection.fitExtent([[50,50],[width-50,height-50]],countiesFeatureCollection);
       projection.fitExtent([[50,50],[width-50,height-50]],countiesFeatureCollection);
       redraw(countiesFeatureCollection,"counties");                                 
       if ( projection.translate().toString() === baseProjection.translate().toString() && projection.scale() === baseProjection.scale() ) 
       {
        zoom(baseProjection,projection.fitExtent([[50,50],[width-50,height-50]],countiesFeatureCollection));
        }
        else 
        {
           var endProjection = d3.geoMercator();
           zoom(projection,endProjection.fitExtent([[50,50],[width-50,height-50]],countiesFeatureCollection));
        }
    

    3-同样的事情应该应用于社区
    var endProjection = d3.geoMercator();
    endProjection.fitExtent([[50,50],[width-50,height-50]],communesfeatureCollection);
    projection.fitExtent([[50,50],[width-50,height-50]],communesfeatureCollection);
    redraw(communesfeatureCollection,"communes");
    if ( projection.translate().toString() === projectioncommune.translate().toString() && projection.scale() === projectioncommune.scale()){
    
      zoom(projectioncommune,projection.fitExtent([[50,50],[width-50,height-50]],communesfeatureCollection));
    }
    
    else {
          var endProjection = d3.geoMercator();
          zoom(projection,endProjection.fitExtent([[50,50],[width-50,height-50]],communesfeatureCollection));
    }
    

    4-然后重新初始化以转到第一步
          // start rendering points again
          baseProjection.fitSize([width,height],regions);
          projection.fitSize([width,height],regions);
          //GENERATE AGAIN THE FIRST MAP 
          dataArray  = data;
          redraw();                                                                 
          zoom(projection,baseProjection);
    

    ATTACHED WORKING PEN

    关于d3.js 单击并应用缩放和平移将位于目标分区内的点分布到触发的分割,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60157295/

    相关文章:

    charts - 如何使用dc.js或d3.js为行图添加轴标签

    d3.js - xAxis 刻度的 D3 组标签

    javascript - D3 稳定排序模拟

    javascript - c3 使用 .toString() 时解析失败

    javascript - 鼠标悬停可显示/隐藏力布局链接

    html - 在 <use> 节点内旋转 SVG 元素

    python - 使用 Pyembroidery 将矢量文件转换为 .DST 刺绣文件

    c# - GLSL "zoom"像模糊

    css - 由于悬停时的过渡/缩放,移动设备上的滚动困难

    android - 如何将缩放控件设置为自定义按钮?