问题描述
我在围绕红点创建 voronoi 多边形时遇到问题。这些点是在每个状态的边界内随机生成的。我正在尝试使用 d3-voronoi 库来这样做,它会在下面的链接中生成图片。有什么建议么?某些状态是多重多边形是否存在问题?
<!DOCTYPE html>
<html>
<head>
<Meta charset="UTF-8">
<title>D3 Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
<script src="https://d3js.org/d3-polygon.v2.min.js"></script>
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/d3-delaunay@5"></script>
<!-- <script src="https://unpkg.com/[email protected]/simplex-noise.js"></script>-->
<!-- <script src="https://unpkg.com/[email protected]/delaunator.min.js"></script>-->
<!--<script src="https://d3js.org/d3-voronoi.v1.min.js"></script>-->
<!--<script src="https://d3js.org/d3-array.v2.min.js"></script>
<script src="https://d3js.org/d3-geo.v2.min.js"></script>-->
<script src="https://d3js.org/topojson.v1.min.js"></script>
<style>
body {
font-family: "Helvetica Neue","Helvetica","Arial",sans-serif;
font-size: 15pt;
}
h1 {
text-align: center;
font-weight: 500;
}
svg {
display: block;
margin: auto;
}
/* Tooltip */
div.tooltip {
position: absolute;
visibility: hidden;
font-size: 60%;
color: #333333;
background-color: #e6e6e6;
opacity: .95;
padding: 5px;
}
button {
padding: 10px 20px;
}
.background {
fill: none;
pointer-events: all;
}
#states {
fill: #aaa;
}
#states .active {
display:none;
}
#state-borders {
fill: none;
stroke: #fff;
stroke-width: 1.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
.county-boundary {
fill: #aaa;
stroke: #fff;
stroke-width: .5px;
}
.county-boundary:hover,.state:hover {
fill: orange;
}
</style>
</head>
<body style="background-color:dimgray;">
<h1>Gerry Data</h1>
<div id="county"></div>
<div id="state"></div>
<div id="us"></div>
<div class="tooltip"></div>
<script>
var data_map = d3.map();
var width = 1920,height = 1200,centered;
var path_gen;
var aa = [];
var count = 0;
//var pop_rate;
//var svg;
//var imageData = polyContext.getimageData(0,width,height);
var state_codes = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];
var state_names =
{
"Alabama" : 9,"Alaska" : 3,"Arizona" : 11,"Arkansas" : 6,"California" : 55,"Colorado" : 9,"Connecticut" : 7,"Delaware" : 3,"district of Columbia" : 3,"Florida" : 29,"Georgia" : 16,"Hawaii" : 4,"Idaho" : 4,"Illinois" : 20,"Indiana" : 11,"Iowa" : 6,"Kansas" : 6,"Kentucky" : 8,"Louisiana" : 8,"Maine" : 4,"Maryland" : 10,"Massachusetts" : 11,"Michigan" : 16,"Minnesota" : 10,"Mississippi" : 6,"Missouri" : 10,"Montana" : 3,"Nebraska" : 5,"Nevada" : 6,"New Hampshire" : 4,"New Jersey" : 14,"New Mexico" : 5,"New York" : 29,"north Carolina" : 15,"north Dakota" : 3,"Ohio" : 18,"Oklahoma" : 7,"Oregon" : 7,"Pennsylvania" : 20,"Rhode Island" : 4,"South Carolina" : 9,"South Dakota" : 3,"Tennessee" : 11,"Texas" : 38,"Utah" : 6,"Vermont" : 3,"Virginia" : 13,"Washington" : 12,"West Virginia" : 5,"Wisconsin" : 10,"Wyoming" : 3,};
d3.queue()
.defer(d3.json,"data/counties_20m.json")
//.defer(d3.json,"https://unpkg.com/us-atlas@1/us/10m.json")
.defer(d3.json,"data/states_20m.json")
.defer(d3.json,"data/us_20m.json")
.defer(d3.csv,"data/pop.csv",function(d){
// Convert to number
d['POpestIMATE2019'] = +d['POpestIMATE2019'];
// Use the county's FIPS countyode to access that county's data
return data_map.set(d['FIPStxt'],d);
})
.await(ready);
function ready(error,cmap,smap,umap,data_csv) {
// How does the data look like?
//console.log(data_csv);
// Unpack the GeoJSON features
var counties = cmap['features'];
var states = smap['features'];
var us = umap['features'];
//var states = smap['features'];
//var country;
//----------------------------------------
// SVG setup
// var width = 1920,// height = 1200;
svg = d3.select('#county').append('svg')
.attr('width',width)
.attr('height',height);
// .call(d3.zoom().on("zoom",function () {
// svg.attr("transform",d3.event.transform)
// }));
// Append empty placeholder g element to the SVG
// g will contain geometry elements
let g = d3.select('#state').append('svg')
.attr('width',height);
let h = d3.select('#us').append('svg')
.attr('width',height);
var hi = svg.append('svg').selectAll('path');
//----------------------------------------
// Geography setup
var proj = d3.geoAlbersUsa()
.scale(2300)
.translate([width/2,height/2]);
path_gen = d3.geoPath(proj);
var voronoi = d3.voronoi()
.extent([[0,0],[width,height]]);
//----------------------------------------
// Scale setup
var colors = ['#f7fbff','#deebf7','#c6dbef','#9ecae1','#6baed6','#4292c6','#2171b5','#084594'];
var all_values = data_map.values().map( function(d){
return d['POpestIMATE2019'];
});
// Quantile scale
var color_scale = d3.scaleQuantile()
.domain(all_values)
.range(colors);
// Linear scale
// var max = d3.max(all_values),// min = d3.min(all_values);
// var color_scale = d3.scaleLinear()
// .domain([min,max])
// .range([colors[0],colors[colors.length-1]]);
// Power scale
// var max = d3.max(all_values),// min = d3.min(all_values);
// var color_scale = d3.scalePow()
// .domain([min,colors[colors.length-1]])
// .exponent(3);
// Check out the color scale
//console.log( color_scale(21) );
//----------------------------------------
// The map,finally!
svg.selectAll('path')
.data(counties)
//.data(states)
.enter()
.append('path')
.attr('d',path_gen)
// use this for layers and chunks
//.attr( "visibility","hidden")
.style('fill',function(d) {
if (d['properties']['STATE'][0] == 0) {
fips_code = d['properties']['STATE'][1] + d['properties']['COUNTY'];
} else {
fips_code = d['properties']['STATE'] + d['properties']['COUNTY'];
}
//console.log(fips_code);
// Color only if the data exists for the FIPS code
if (data_map.has(fips_code)) {
// Get the entire row of poverty data for each FIPS code
pop_data = data_map.get(fips_code);
//console.log(pop_data);
// Get the specific feature
data = pop_data['POpestIMATE2019'];
return color_scale(data);
};
})
.style('opacity',1)
.style('stroke','black')
.style('stroke-width',"1px")
.style('stroke-opacity',1)
.on('mouSEOver',function(d) {
// Make the county color darker
d3.select(this)
.style('opacity',0.5);
var area = d['properties']['CENSUSAREA'];
// Unload data
if (d['properties']['STATE'][0] == 0) {
fips_code = d['properties']['STATE'][1] + d['properties']['COUNTY'];
} else {
fips_code = d['properties']['STATE'] + d['properties']['COUNTY'];
}
if (data_map.has(fips_code)) {
pop_data = data_map.get(fips_code);
name = pop_data['CTYNAME'];
state = pop_data['STNAME'];
pop_rate = pop_data['POpestIMATE2019'];
}
// var p = d3.polygonContains(d['geometry']['coordinates'][0],[ -98.5795,39.8283 ]);
// console.log(p);
// For some reason,the area cannot be found with separated lands
// if-else to deal with islands since there is an extra array parameter for them
// d3.polygonArea(d['geometry']['coordinates'][0]) -> d3.polygonArea(d['geometry']['coordinates'][0][0])
var ose = 0;
if (d['geometry']['type'] == "Multipolygon") {
for (k = 0; k < d['geometry']['coordinates'].length; k++) {
ose += d3.polygonArea(d['geometry']['coordinates'][k][0]);
}
}
else {
ose = d3.polygonArea(d['geometry']['coordinates'][0]);
}
//console.log(d3.polygonHull(d['geometry']['coordinates'][0]));
var str = "2";
// Show the tooltip
d3.select('.tooltip')
.style('visibility','visible')
.style('top',d3.event.pageY+10 + 'px')
.style('left',d3.event.pageX+10 + 'px')
.html('<strong>' + name + ',' + state + '</strong><br />Population (2019): ' +
pop_rate + ' people<br />Area: ' + area + ' mi' + str.sup() +
'<br />Population Denstiy: ' + (pop_rate/area).toFixed(3) + ' p/mi' + str.sup()
+ '<br />' + ose);
})
.on('mouSEOut',function(d) {
// Make the county usual opacity again
d3.select(this)
.style('opacity',1);
// Hide the tooltip
d3.select('.tooltip')
.style('visibility','hidden');
})
.each(function (d) {
//39.8283° N,98.5795° W
// Y Axis: 18 N to 72 N
// X Axis: 173 E (Western) to 67 W (Eastern)
var tmp = [];
var contain = false;
if (d['properties']['STATE'][0] == 0) {
fips_code = d['properties']['STATE'][1] + d['properties']['COUNTY'];
} else {
fips_code = d['properties']['STATE'] + d['properties']['COUNTY'];
}
if (data_map.has(fips_code)) {
pop_data = data_map.get(fips_code);
state = pop_data['STNAME'];
pop_rate = pop_data['POpestIMATE2019'];
}
var Votes = state_names[state];
//console.log(Votes);
for (i = 0; i < 0; i++) {
tmp = [-1 * (Math.random() * (173 - 66) + 66),Math.random() * (72 - 18) + 18];
if (d['geometry']['type'] == "Multipolygon") {
for (k = 0; k < d['geometry']['coordinates'].length; k++) {
contain = d3.polygonContains(d['geometry']['coordinates'][k][0],tmp);
// console.log(d['properties']['NAME']);
// console.log(d['geometry']['coordinates'].length);
if (contain) {
aa.push(tmp);
//console.log(aa);
break;
// console.log(d['properties']['NAME']);
}
// else {
// //i -= 1;
// console.log(i);
// }
}
}
else {
contain = d3.polygonContains(d['geometry']['coordinates'][0],tmp);
if (contain) {
aa.push(tmp);
}
// else {
// //i -= 1;
// console.log(i);
// }
}
}
svg.selectAll("circle")
.data(aa).enter()
.append("circle")
.attr("cx",function (d) {
count += 1;
// console.log(count);
if (proj(d) == null) {
//console.log(proj(d));
return;
}
// console.log(proj(d));
return proj(d)[0];
})
.attr("cy",function (d) {
if (proj(d) == null) {
//console.log(proj(d));
return;
}
return proj(d)[1];
})
.attr("r","10px")
.attr("fill","red");
});
//.on("click",transform);
// .call(function (d) {
//
//
//
// //aa = [-98.5795,39.8283];
// if (d3.polygonContains(d['geometry']['coordinates'],tmp) == true) {
// aa.push(tmp);
// d3.geo
// }
// else {
// continue;
// }
//
// // aa.push([-1 * (Math.random() * (125.5795 - 75.5795) + 75.5795),Math.random() * (48.8283 - 30.8283) + 30.8283]);
// // console.log(aa);
// }
//
//
// });
g.selectAll("path")
.data(states) // Bind TopoJSON data elements
// pass through what objects you want to use -- in this case we are doing county lines
.enter().append("path")
.attr("d",path_gen)
//.attr("visibility","hidden")
.style("fill","none")
.style("stroke","white")
.style("stroke-width","1px")
.each(function (d) {
var tmp;
var contain = false;
if (d['properties']['STATE'][0] == 0) {
fips_code = d['properties']['STATE'][1] + d['properties']['COUNTY'];
} else {
fips_code = d['properties']['STATE'] + d['properties']['COUNTY'];
}
if (data_map.has(fips_code)) {
pop_data = data_map.get(fips_code);
state = pop_data['STNAME'];
pop_rate = pop_data['POpestIMATE2019'];
}
var Votes = state_names[d['properties']['NAME']];
//console.log(d['properties']['NAME'] + ': ' + Votes);
var vc = 0;
var bounds = d3.geoBounds(d);
//console.log(bounds);
while (vc < Votes) {
if (d['properties']['NAME'] == 'Alaska') {
tmp = [-1 * (Math.random() * (bounds[1][0] - bounds[0][0]) + bounds[0][0]),Math.random() * (bounds[1][1] - bounds[0][1]) + bounds[0][1]];
}
else {
tmp = [Math.random() * (bounds[1][0] - bounds[0][0]) + bounds[0][0],Math.random() * (bounds[1][1] - bounds[0][1]) + bounds[0][1]];
}
//console.log(tmp);
if (d['geometry']['type'] == "Multipolygon") {
for (k = 0; k < d['geometry']['coordinates'].length; k++) {
contain = d3.polygonContains(d['geometry']['coordinates'][k][0],tmp);
// console.log(d['properties']['NAME']);
// console.log(d['geometry']['coordinates'].length);
if (contain) {
aa.push(tmp);
vc += 1;
console.log(d['properties']['NAME'] + ' 1: ' + vc);
//console.log(aa);
break;
// console.log(d['properties']['NAME']);
}
// else {
// //i -= 1;
// console.log(i);
// }
}
}
else {
contain = d3.polygonContains(d['geometry']['coordinates'][0],tmp);
if (contain) {
aa.push(tmp);
vc += 1;
console.log(d['properties']['NAME'] + ' 2: ' + vc);
}
// else {
// //i -= 1;
// console.log(i);
// }
}
console.log(d['properties']['NAME'] + ' : ' +vc);
}
//console.log(voronoi(aa).polygons());
// Adding data to represent the Voronoi
// and displaying it
g.selectAll("circle")
//.data([[-98.5795,39.8283],[-96.5795,41.8283]]).enter()
.data(aa).enter()
.append("circle")
.attr("cx",function (d) {
count += 1;
// console.log(count);
if (proj(d) == null) {
//console.log(proj(d));
return;
}
// console.log(proj(d));
return proj(d)[0];
})
.attr("cy",function (d) {
if (proj(d) == null) {
//console.log(proj(d));
return;
}
return proj(d)[1];
})
.attr("r","3px")
.attr("fill","red");
g.append('g').selectAll('path')
.data(voronoi.polygons(aa))
.enter()
.append("path")
.attr("stroke","black")
.attr("stroke-width","1px")
.attr("fill","none")
.attr("d",(d) => {
return d ? ("M" + d.join("L") + "Z") : null;
});
});
// voronoiDiagram = d3.voronoi();
// voronoiDiagram(aa);
// Using the d3.voronoi() function
// to create a Voronoi diagram
// .call(function (d) {
// //39.8283° N,98.5795° W
// d3.select(this);
//
// var p = d3.polygonContains(d['objects']['counties']['geometries'],39.8283 ]);
// console.log(p);
// });
h.selectAll("path")
.data(us) // Bind TopoJSON data elements
// pass through what objects you want to use -- in this case we are doing county lines
.enter().append("path")
.attr("d",path_gen)
.style("fill","1px");
};
// function clicked(d) {
// var x = 0,// y = 0;
//
// // If the click was on the centered state or the background,re-center.
// // Otherwise,center the clicked-on state.
// if (!d || centered === d) {
// centered = null;
// } else {
// var centroid = path_gen.centroid(d);
// x = width / 2 - centroid[0];
// y = height / 2 - centroid[1];
// centered = d;
// }
//
// // Transition to the new transform.
// svg.transition()
// .duration(750)
// .attr("transform","translate(" + x + "," + y + ")");
// }
</script>
</body>
</html>
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)