Reactjs + D3 v4投影问题 [英] Reactjs + D3 v4 projection issue

查看:242
本文介绍了Reactjs + D3 v4投影问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在这里了10个小时了,我开始认为这是一个真的很小或基本的东西我在这里失踪。



这是一个简单的组件,所有我想要的是通过geoAlbersUsa渲染我们的地图与d3版本4,并在一个面板中的PROJECT它的SCALED。如果我删除投影所有的工作伟大,我得到地图。在任何形状或形式的时刻,我做投影它只是显示一个彩色矩形。这里是代码:

  import从'react'反应
import * as d3 from'd3';
import * as topojson from'topojson';
import {Panel,Alert} from'react-bootstrap';

class MapBlock extends React.Component {
constructor(props){
super(props);
this.state = {
states:[]
};
this.projection = d3.geoAlbersUsa()。scale(1000);
this.geoPath = d3.geoPath()。projection(this.projection);
}

componentDidMount(){
d3.json(https://d3js.org/us-10m.v1.json,function(error,us){
if(error)throw error;
this.setState({states:topojson.feature(us,us.objects.states).features})
} .bind(this)
}

render(){
let {states} = this.state;
return(
< Panel>
< svg width =550height =430>
< g className =states>
{
states.map((feature,index)=>< path key = {index} d = {this.geoPath(feature)} />)
}
; / g>
< / svg>
< / Panel>

}
}

导出默认MapBlock

html也很简单:

 < Grid> 
< Row className =show-grid>
< Col sm = {12} md = {6}>
< MapBlock />
< / Col>
< Col sm = {12} md = {6}>
一些文本...
< / Col>
< / Row>
< / Grid>

任何帮助,请告知我,如果细节不够。提前感谢。



更新:



以处理来自@Mark的初始评论的建议。




我试过这样:

  var states = topojson.feature(us,us.objects.states).features; 

var width = 560;
var height = 300;

var b = path.bounds(states [0]),
s = .98 / Math.max((b [1] [0] -b [0] [0] / width,(b [1] [1] -b [0] [1])/ height),
t = [(width - s *(b [1] [0] + b [0] ])/ 2,(height-s *(b [1] [1] + b [0] [1]

//更新投影以使用计算比例&翻译。
projection
.scale(s)
.translate(t);

vis.selectAll(path)
.data(states)
.enter()
.append(path)
.attr (class,states)
.attr(d,path);

为此,我改为渲染为:

 < Panel> 
< div id =vis>< / div>
< / Panel>

我不知道这里是 path.bounds(states [0] )是我的一个猜测,所以我可以看到,如果它将工作基于面板边界我有。我做了缩放它,你可以看到从图像,但我想不完全是:)

解决方案

code> d3 版本4有一个更简单的方法来将投影拟合到边界空间。他们在投影中引入了 fitSize 方法



这里是操作中:



 <!DOCTYPE html>< html lang =en>< head> < meta charset =utf-8/> < meta name =viewportcontent =width = device-width,initial-scale = 1/> < title> jQuery UI Resizable  - 默认功能< / title> < script data-require =d3@4.0.0data-semver =4.0.0src =https://d3js.org/d3.v4.min.js>< / script> < script data-require =d3@4.0.0data-semver =4.0.0src =https://d3js.org/topojson.v2.min.js>< / script> < link rel =stylesheethref =// code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css/> < style> #resizable {width:150px; height:150px; padding:0.5em; }< / style> < script src =https://code.jquery.com/jquery-1.12.4.js>< / script> < script src =https://code.jquery.com/ui/1.12.1/jquery-ui.js>< / script> < script> $(function(){$(#resizable)。resizable({resize:function(event,ui){drawMap(ui.size.width,ui.size.height);}}); var svg = d3。 select(#resizable).append(svg),path = svg.append(path); var states; d3.json('https://jsonblob.com/api/9e44a352-eb09-11e6- 90ab-059f7355ffbc',function(error,data){states = topojson.feature(data,data.objects.states); drawMap(150,150);}); function drawMap(w,h){svg.attr width',w).attr('height',h); var projection = d3.geoAlbersUsa().scale(1).fitSize([w,h],states); var geoPath = d3.geoPath (投影); path .datum(states).attr(d,geoPath);}}); < / script>< / head>< body> < div id =resizableclass =ui-widget-content>< / div>< / body>< / html>  

div>


I have been on this for 10 hours now and I am starting to think that it is either something really small or something fundamental I am missing here.

This is a simple component that all I want is to render the us map with the d3 version 4 via geoAlbersUsa and PROJECT it in a panel so that it is SCALED. If I remove the projection all works great and I get the map. The moment in any shape or form I do the projection it simply shows a colored rectangle. Here is the code:

import React from 'react';
import * as d3 from 'd3';
import * as topojson from 'topojson';
import { Panel, Alert } from 'react-bootstrap';

class MapBlock extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        states: []
    };
    this.projection = d3.geoAlbersUsa().scale(1000);
    this.geoPath = d3.geoPath().projection(this.projection);
}

componentDidMount() {
    d3.json("https://d3js.org/us-10m.v1.json", function(error, us) {
        if (error) throw error;
        this.setState({states: topojson.feature(us, us.objects.states).features})
    }.bind(this));
}

render() {
    let { states } = this.state;
    return (
        <Panel>
            <svg width="550" height="430">
                <g className="states">
                    {
                        states.map((feature, index) => <path key={index} d={this.geoPath(feature)} />)
                    }
                </g>
            </svg>
        </Panel>
    )
}
}

export default MapBlock

The html is also pretty simple:

<Grid>
    <Row className="show-grid">
        <Col sm={12} md={6}>
            <MapBlock />
        </Col>
        <Col sm={12} md={6}>
            Some Text ...
        </Col>
   </Row>
</Grid>

Any help is appreciated and please let me know if the details are not enough. Thanks in advance.

UPDATE:

Here is what I get now trying to work with the suggestion from the initial comment from @Mark.

I tried something like this:

 var states = topojson.feature(us, us.objects.states).features;

 var width  = 560;
 var height = 300;

 var b = path.bounds(states[0]),
     s = .98 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1]  - b[0][1]) / height),
     t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];

 // Update the projection to use computed scale & translate.
 projection
     .scale(s)
     .translate(t);

 vis.selectAll("path")
     .data(states)
     .enter()
     .append("path")
     .attr("class", "states")
     .attr("d", path);

For this I changed to render to be just:

<Panel>
    <div id="vis"></div>
</Panel>

What I am not sure here is path.bounds(states[0]) was a guess of mine just so I can see if it would work based on the panel boundary I have. I did "scale" it as you can see from the image but I guess not exactly :)

解决方案

I just realized that d3 version 4 has an even easier way to fit a projection to a bounding space. They've introduced the fitSize method on the projection.

Here it is in action:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>jQuery UI Resizable - Default functionality</title>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/topojson.v2.min.js"></script>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />
  <style>
    #resizable {
      width: 150px;
      height: 150px;
      padding: 0.5em;
    }
  </style>

  <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
  <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

  <script>
    $(function() {
      $("#resizable").resizable({
        resize: function(event, ui) {
          drawMap(ui.size.width, ui.size.height);
        }
      });

      var svg = d3.select("#resizable")
        .append("svg"),
        path = svg.append("path");

      var states;
      d3.json('https://jsonblob.com/api/9e44a352-eb09-11e6-90ab-059f7355ffbc', function(error, data) {

        states = topojson.feature(data, data.objects.states);
        drawMap(150, 150);

      });

      function drawMap(w, h) {

        svg.attr('width', w)
          .attr('height', h);

        var projection = d3.geoAlbersUsa()
          .scale(1).fitSize([w, h], states);

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

        path
          .datum(states)
          .attr("d", geoPath);

      }


    });
  </script>
</head>

<body>
  <div id="resizable" class="ui-widget-content"></div>
</body>

</html>

这篇关于Reactjs + D3 v4投影问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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