3

我想绘制数据来自各个 topoJSON 文件的多个国家/地区。包含这些国家的地图应位于屏幕中间(居中),并且应设置适当的比例以使地图填满整个窗口。

我已经尝试过的:

  • 要获得path.bounds(d)每个国家的边界​​框并为每一侧(顶部、左侧、底部、右侧)分别使用最大值和最小值,并提供从path.centroid(d)到的平均中心projection.center()。这没有用。

  • 应用projection.fitExtent([[x0,y0],[x1,y1]],geojsonObject)projection.fitSize([width,height],geojsonObject)按照解决方案中的建议。在这里,我无法按照描述创建 featureCollection 并使用它来创建地图。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="https://d3js.org/d3.v5.min.js"></script>
        <script src="https://d3js.org/topojson.v2.min.js"></script>
        <style type="text/css">

    .svg-container {
        display: inline-block;
        position: relative;
        width: 100%;
        padding-bottom: 100%;
        vertical-align: top;
        overflow: hidden;
    }
    .svg-content {
        display: inline-block;
        position: absolute;
        margin: 0;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

        </style>
    </head>
    <body>
        <div id="container" class="svg-container"> </div>

        <script type="text/javascript">
                var width = 600; //for simplicity set to fixed value
                var height = 600; //for simplicity set to fixed value

                var projection = d3.geoMercator();

                var path = d3.geoPath()
                    .projection(projection);

                var svg = d3.select("#container").append("svg")
                      .classed("svg-content", true)
                      .attr("width", width)
                      .attr("height", height);

            //Example data
            var countries = ['germany', 'italy', 'switzerland', 'france']; 

            function loadJsonFiles() {

                  var files = ["https://api.myjson.com/bins/1b0ddz", 
                       "https://api.myjson.com/bins/11jkvb",
                       "https://api.myjson.com/bins/96x1j",
                       "https://api.myjson.com/bins/sspzr"];

                  var promises = [];
                  var allCountryData = [];

                  files.forEach(function(url) {
                          promises.push(d3.json(url))
                  });

                  return Promise.all(promises)
                      .then(function(countryData) {

                      for (var i = 0; i < countryData.length; i++) {

                        allCountryData.push(countryData[i]);

                      }
                    return allCountryData;
                  });
            }

            loadJsonFiles().then(function(allCountryData) {

                  var allBounds = [];
                  var objName; 
                  var countryData;

                  for (var i = 0; i < allCountryData.length; i++) {

                      objName = allCountryData[i].objects[countries[i]];
                      countryData = topojson.feature(allCountryData[i], objName);

                      //How can I use the right projection parameters for all country data loaded from topojson (outside the loop)?
                      projection
                          .scale(1000) //How to set this programmatically?
                          .center([8,47]) //How to set this programmatically?
                          .translate([width / 2, height / 2]);
                      //How can I append all the country data to the svg (outside the loop)?
                      svg.append("path")
                          .datum(countryData)
                          .attr("d", path);
                  }

            });

        </script>
    </body>
</html>   

我认为应该可以通过 Andrew Reid 在这篇文章中提供的建议来解决这个问题,fitSize或者fitExtent。但是我无法将其应用于我的问题。这也是我解决问题的首选方式。

或者,是否可以用center()and解决这个问题scale()?我将如何确定我必须提供哪些坐标center()以及 . 的值scale()?非常感谢您的帮助,我尝试了几天但没有成功。

4

1 回答 1

3

projection.fitSize并且projection.fitExtent都采用单个 geojson 对象,而不是 geojson 对象数组。幸运的是,geojson 有一个对象类型,它可以包含我们需要的任意数量的子对象:a featureCollection.

我看到您正在使用 topojson,topojson.feature返回一个 geojson 对象。在您的情况下,每次使用 topojson 时都会返回一个特征集合。

为了使用 fitSize/fitExtent 我们需要创建一个包含所有单个特征的 geojson 对象,一个特征集合会这样做,结构如下:

featureCollection = {
  "type":"FeatureCollection",
  "features": [ ... ]
}

我们可以使用以下内容构建特征集合:

var featureCollection = {type:"FeatureCollection","features":[]}

for (var i = 0; i < allCountryData.length; i++) {

     objName = allCountryData[i].objects[countries[i]];
     countryData = topojson.feature(allCountryData[i], objName);

     featureCollection.features.push(...countryData.features)

}

现在我们可以将这个特征集合传递给 fitSize/fitExtent

projection.fitSize([width,height],featureCollection);

应该是这样的:

                var width = 400; //for simplicity set to fixed value
                var height = 200; //for simplicity set to fixed value

                var projection = d3.geoMercator();

                var path = d3.geoPath()
                    .projection(projection);

                var svg = d3.select("#container").append("svg")
                      .classed("svg-content", true)
                      .attr("width", width)
                      .attr("height", height);

            //Example data
            var countries = ['germany', 'italy', 'switzerland', 'france']; 

            function loadJsonFiles() {

                  var files = ["https://api.myjson.com/bins/1b0ddz", 
                       "https://api.myjson.com/bins/11jkvb",
                       "https://api.myjson.com/bins/96x1j",
                       "https://api.myjson.com/bins/sspzr"];

                  var promises = [];
                  var allCountryData = [];

                  files.forEach(function(url) {
                          promises.push(d3.json(url))
                  });

                  return Promise.all(promises)
                      .then(function(countryData) {

                      for (var i = 0; i < countryData.length; i++) {

                        allCountryData.push(countryData[i]);

                      }
                    return allCountryData;
                  });
            }

            loadJsonFiles().then(function(allCountryData) {

                var allBounds = [];
                var objName; 
                var countryData;


				var featureCollection = {type:"FeatureCollection","features":[]}
				for (var i = 0; i < allCountryData.length; i++) {

					 objName = allCountryData[i].objects[countries[i]];
					 countryData = topojson.feature(allCountryData[i], objName);

					 featureCollection.features.push(...countryData.features)

				}
				
				projection.fitSize([400,200],featureCollection)
				
				svg.selectAll("path")
				  .data(featureCollection.features)
				  .enter()
				  .append("path")
				  .attr("d",path);

            });
svg {
   border: 1px solid black;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<div id="container" class="svg-container"> </div>

我保留了您的代码,只是我使用了不同的 for 循环(如上所示)来创建功能集合,而不是循环附加每个路径,我使用了输入选择。最后,我更改了大小和位置,使其更适合片段视图。

于 2019-06-09T17:45:45.237 回答