双击欧洲数据可视化 [英] Double clicking Europe data visualization

查看:121
本文介绍了双击欧洲数据可视化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个可以放大的欧洲Choropleth地图. 我还希望看到,当用户双击一个国家时,该国家会被缩放并划分为多个区域(NUTS 2),每个区域都会根据第二种颜色进行着色.

这里有一个例子:

假设欧洲由5个国家组成:Country1,...,Country5. 每个国家的颜色均按照第一个度量标准(假设居民数量)为准. 当用户双击Country4时,地图将放大,以使Country4在屏幕的中央并被完全查看.邻近的国家可能被裁剪和模糊化.

Country4现在显示为由其区域(R1,...,R6)组成.这些区域根据第二种方法(假设人均收入)进行着色. 在第二种情况下,我希望未选择的国家(因此,国家1、2、3和5)仍根据度量1进行着色.

所以我想要像这样的,但是能够双击并查看每个国家/地区.

我该怎么做? 我还没有找到对我有用的例子.

我找到了这些json文件此文件代表了nuts2(区域)和代表nuts0(国家).

如何将两者合并?这个想法是从nuts2.json开始并添加nuts0.json的信息,但是我该如何使用geometriesarcs?我不想造成不一致..

解决方案

这是一个非常广泛的问题,其中包含几个问题和目标(地图缩放,创建合奏,两层地图),因此,任何答案都将是广泛的-但不一定无济于事.我的答案不会解决问题中的每个问题,但应有助于建立您的最终目标.我将重点关注似乎是关键问题的问题:

我还希望看到当用户双击一个 国家/地区,将所讨论的国家/地区放大并划分为多个区域, 每个都根据第二种颜色着色.

允许您说也",这表明这是次要的,但是您的图片和标题似乎更关注两层效果,并且有很多例子和关于问题的讨论,但是很少涉及交互式两层地图.

我认为关键的挑战是细分区域,要做到这一点,您将需要在父区域和子区域之间使用某种公共标识符.如果需要,可以在geojson或topojson中添加必要的标识符.理想情况下,您的geojson可能看起来像:

父母/国家/地区:

{ 
    "type":"Feature",
    "properties"{ "country":NAME ... },
    "geometry": { ... }
} 

儿童/地区:

{ 
    "type":"Feature",
    "properties"{ "country":NAME, "regionName": NAME ... },
    "geometry": { ... }
} 

单击某个国家(或任何其他事件,例如双击)时,您要基于共享标识符绘制子区域:

country.on("click", function(d) {
  // remove other regions
  d3.selectAll(".region").remove();

  // filter out relevant regions of a geojson of all regions 
  var countryRegions = geojson.features.filter(function(region) { 
    return region.properties.country == d.properties.country;
  })
  // append the regions
  svg.selectAll(".region")
   .data(countryRegions)
   .enter()
   .append()
   .attr("class",".region")
})

如果您有带有标准化命名的geojson文件,则可以将文件名用作共享属性,并按以下方式进行操作:

country.on("click", function(d) {
  // remove other regions
  d3.selectAll(".region").remove();

  // get appropriate geojson:
  d3.json(d.properties.country+".json", function(error, regions) {

     // draw region features

  }) 

})

您可以通过添加以下内容来提高点击/其他事件的国家/地区的透明度:country.style("opacity",0.4)在点击/其他事件中,模糊会增加一些复杂性.两者都应增强两个层次的效果.与国家打交道时不需要裁剪-国家很少重叠,无论如何,通常会在旧特征的上方绘制新特征(这消除了坐标不精确导致的任何视觉重叠).

这就是两层效果的全部内容,使用相同的原理,您可以轻松创建三层地图,在选定区域中填充子区域.


在此基础上,我将简要介绍一下缩放:

使用包含该区域的geojson/topojson,然后可以更改投影以反映要素的范围-允许您缩放到那些要素:

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

请注意,如果过滤一系列功能,则 fitSize "https://github.com/d3/d3-geo#projection_fitExtent" rel ="nofollow noreferrer"> fitExtent 无效,除非您将要素放入要素集中(并且都需要v4):

var featureCollection = {type:"FeatureCollection",features:features};

要完成平滑缩放,您需要使用 transition.attrTween .这有点棘手,因为您需要同时插入投影平移和投影比例(并取决于地图的投影和类型,也许是旋转).

或者,您可以通过操作svg来缩放,这里有许多示例和有关如何实现此效果的问题(我在下面的示例中使用另一种方法).

以上内容可让您:缩放区域,绘制相关区域以及在区域/视图之间切换.

我使用虚拟地理数据制作了一个简单的通用示例,该数据是可在此处进行操作(使用单击事件),下面对关键部分进行了更详细的注释(过渡功能除外,请参见示例以查看它).此示例将需要进行大量调整以匹配您的预期数据和可视化.

我正在使用以下代码中未声明的一些变量(请参见 example (完整代码),但它们大多是标准的:geo​​Path(path),geoProjection(projection),宽度,高度等,还有baseProjection是起始投影.我也在使用伪数据,因此我使用的是d3.geoIdentity而不是更标准的投影.

// get the parent geojson
d3.json("geojson.json", function(error, geojson) {
  if (error) throw error;

  // get the regions:
  d3.json("geojsonSubdivisions.json", function(error, subdivisions) {
    if (error) throw error;

  // a color scale for the countries:
  var color = d3.scaleLinear().range(["steelblue","darkblue"]).domain([0,4]);
  // a color scale for the regions:
  var subdivisionColor = ["lightsalmon","salmon","coral"];

  // refine the two projections, one for the default/base, and one for the current      
  baseProjection.fitSize([width,height],geojson);
  projection.fitSize([width,height],geojson);

  // append the countries:
  svg.append("g")
    .attr("class", "topLevel")
    .selectAll("path")
    .data(geojson.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)
    // style on mouseover:
    .on("mouseover", function() {
        d3.select(this)
          .style("stroke-width",15)
          .raise();
    })
    // undo mouseover styles:
    .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(".subdivision")
          .remove();

        // get new features:
        var features = subdivisions.features.filter(function(feature) { return feature.id == d.id });

        // draw new features
        svg.selectAll(null)
          .data(features)
          .enter()
          .append("path")
          .attr("class","subdivision")
          .attr("fill", function(d,i) { return subdivisionColor[i] })
          .attr("d", path)
          .style("stroke","black")
          .style("stroke-width",0)
          .on("click", function() {
            zoom(projection,baseProjection); // zoom out when clicked
            d3.selectAll(".subdivision")     
              .remove();    // remove regions when clicked
          })
          // style on mouseover
          .on("mouseover", function() {
                d3.select(this)
                  .style("stroke-width",5)
                  .raise(); // raise it so stroke is not under anything
          })
          // undo style changes on mouseout
          .on("mouseout", function(d,i) {
                d3.select(this)
                  .style("stroke-width", 0 );
          })
          .raise()

        // make a feature collection of the regions:
        var featureCollection = { "type":"FeatureCollection", "features": features }

        // zoom to the selected area:
           // if current projection is default projection:
        if ( projection.translate().toString() === baseProjection.translate().toString() && projection.scale() === baseProjection.scale() ) {
            zoom(baseProjection,projection.fitExtent([[50,50],[width-50,height-50]],featureCollection));
        }
        // current projection != default, zoom out and then in:
        else {
            // provide an end projection point for the transition:
            var endProjection = d3.geoIdentity()
              .reflectY(true)
              .fitExtent([[50,50],[width-50,height-50]],featureCollection)

            zoom(projection,endProjection,baseProjection);
        }
    }); 
  });
});

I would like to create a choropleth map of Europe with the possibility of zooming. I would also like to see that when the user double-clicks on a country, the country in question is zoomed and divided into regions (NUTS 2), each of which is colored according to a second measure.

Here an example:

Suppose that Europe is composed of 5 countries: Country1, ..., Country5. Each country is colored according to the first measure (suppose the number of inhabitants). When the user double-clicks on Country4, the map is zoomed so that Country4 is at the center of the screen and viewed entirely. The adjacent countries are possibly cropped and blurred.

Country4 is now displayed as composed of its regions (R1, ..., R6). These regions are colored according to the second measure (suppose the per capita income). In the second situation, I would like the non-selected countries (therefore Country1, 2, 3 and 5) to be still colored according to measure 1.

So I would like something like this but with the ability to double click and view each country in more detail.

How can I do something like that? I haven't found examples that could be useful to me.

I found these json files and this one that I think they are useful (but I don't know how to use them).

Thank you


I found this file representing the nuts2 (regions) and this representing the nuts0 (countries).

How can I merge both? The idea is to start from nuts2.json and add the information of nuts0.json, but how can I do with geometries and arcs? I wouldn't like to create inconsistencies..

解决方案

This is a very broad question and contains several questions and goals (map zooming, choropleth creation, two tiered maps), consequently, any answer will be broad - but not necessarily unhelpful. My answer will not solve every issue in the question, but should help in creating your intended final vision. I'm going to focus on what appears to be the key question:

I would also like to see that when the user double-clicks on a country, the country in question is zoomed and divided into regions, each of which is colored according to a second measure.

Granted you say "also" which suggests this is secondary, but your pictures and title appear to be more concerned with the two tier effect, and there are many examples and questions of choropleths, but few on interactive two tiered maps.

I take it the key challenge is subdividing the regions, to do this you will need some sort of common identifier between parent and child regions. In your geojson or topojson you could add necessary identifiers if needed. Ideally, your geojson might look like:

Parent/Country:

{ 
    "type":"Feature",
    "properties"{ "country":NAME ... },
    "geometry": { ... }
} 

Child/Region:

{ 
    "type":"Feature",
    "properties"{ "country":NAME, "regionName": NAME ... },
    "geometry": { ... }
} 

When a country is clicked on (or any other event such as double click), you want to draw the children regions based on the shared identifier:

country.on("click", function(d) {
  // remove other regions
  d3.selectAll(".region").remove();

  // filter out relevant regions of a geojson of all regions 
  var countryRegions = geojson.features.filter(function(region) { 
    return region.properties.country == d.properties.country;
  })
  // append the regions
  svg.selectAll(".region")
   .data(countryRegions)
   .enter()
   .append()
   .attr("class",".region")
})

If you had geojson files with standardized naming, you could use the file name as the shared property, doing something along the lines of:

country.on("click", function(d) {
  // remove other regions
  d3.selectAll(".region").remove();

  // get appropriate geojson:
  d3.json(d.properties.country+".json", function(error, regions) {

     // draw region features

  }) 

})

You could up the transparency of the countries on click/other event by adding something like: country.style("opacity",0.4) in the on click/other event, a blur will add a bit more complexity. Either should enhance the two tiered effect. Cropping is unnecessary when dealing with countries - countries rarely overlap, and in any event, new features are generally drawn above old features (which eliminates any visual overlap resulting from imprecision of coordinates).

That is all there is to a two tiered effect, using the same principle you could easily create a three tiered map, populating a selected region with subregions.


Building on that I'll very briefly touch on zooming:

Using the geojson/topojson that contains the region, you can then change the projection to reflect the extent of the features - which allows you to zoom to those features:

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

Note that if filtering an array of features, fitSize of fitExtent won't work unless you place the features in a feature collection (and both require v4):

var featureCollection = {type:"FeatureCollection",features:features};

To accomplish a smooth zoom, you'll need to transition the projection with transition.attrTween. This is a bit tricky, as you need to interpolate both projection translation and projection scale (and depending on the map projection and type, perhaps rotation).

Alternatively, you could zoom by manipulating the svg instead, there are many examples of and questions about how to achieve this effect (I use the other approach in my example below).

The above will let you: zoom to regions, draw relevant regions, and transition between regions/views.

I've produced a simple generic example, using dummy geographic data, which is operational here, (using single click events), the key parts are more heavily commented below (excepting the transition function, see the example to see it). This example will require heavy adaptation to match your intended data and visualization.

I am using a few variables that are not declared in the below code (See the example for the full code), but they are mostly standard: the geoPath (path), the geoProjection (projection), width, height, etc, but also baseProjection which is the starting projection. I'm also using dummy data, hence my use of d3.geoIdentity rather than a more standard projection.

// get the parent geojson
d3.json("geojson.json", function(error, geojson) {
  if (error) throw error;

  // get the regions:
  d3.json("geojsonSubdivisions.json", function(error, subdivisions) {
    if (error) throw error;

  // a color scale for the countries:
  var color = d3.scaleLinear().range(["steelblue","darkblue"]).domain([0,4]);
  // a color scale for the regions:
  var subdivisionColor = ["lightsalmon","salmon","coral"];

  // refine the two projections, one for the default/base, and one for the current      
  baseProjection.fitSize([width,height],geojson);
  projection.fitSize([width,height],geojson);

  // append the countries:
  svg.append("g")
    .attr("class", "topLevel")
    .selectAll("path")
    .data(geojson.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)
    // style on mouseover:
    .on("mouseover", function() {
        d3.select(this)
          .style("stroke-width",15)
          .raise();
    })
    // undo mouseover styles:
    .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(".subdivision")
          .remove();

        // get new features:
        var features = subdivisions.features.filter(function(feature) { return feature.id == d.id });

        // draw new features
        svg.selectAll(null)
          .data(features)
          .enter()
          .append("path")
          .attr("class","subdivision")
          .attr("fill", function(d,i) { return subdivisionColor[i] })
          .attr("d", path)
          .style("stroke","black")
          .style("stroke-width",0)
          .on("click", function() {
            zoom(projection,baseProjection); // zoom out when clicked
            d3.selectAll(".subdivision")     
              .remove();    // remove regions when clicked
          })
          // style on mouseover
          .on("mouseover", function() {
                d3.select(this)
                  .style("stroke-width",5)
                  .raise(); // raise it so stroke is not under anything
          })
          // undo style changes on mouseout
          .on("mouseout", function(d,i) {
                d3.select(this)
                  .style("stroke-width", 0 );
          })
          .raise()

        // make a feature collection of the regions:
        var featureCollection = { "type":"FeatureCollection", "features": features }

        // zoom to the selected area:
           // if current projection is default projection:
        if ( projection.translate().toString() === baseProjection.translate().toString() && projection.scale() === baseProjection.scale() ) {
            zoom(baseProjection,projection.fitExtent([[50,50],[width-50,height-50]],featureCollection));
        }
        // current projection != default, zoom out and then in:
        else {
            // provide an end projection point for the transition:
            var endProjection = d3.geoIdentity()
              .reflectY(true)
              .fitExtent([[50,50],[width-50,height-50]],featureCollection)

            zoom(projection,endProjection,baseProjection);
        }
    }); 
  });
});

这篇关于双击欧洲数据可视化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆