我们如何将颜色从顺序比例加载到 D3 v5 中的地图中?
How can we load color from a sequential scale into a map in D3 v5?
我正在 D3 v5 中构建等值线,但是当我尝试加载与红色顺序比例对应的状态颜色时,没有任何反应,所有状态都保持灰色,如下图所示:
这是我用来制作地图的代码:
const second_height = 850;
const projection = d3.geoMercator()
.center([ 132, -28 ])
.translate([ second_width/2, second_height/2 ])
.scale(1000);
const path = d3.geoPath().projection(projection);
//Color scale that I am trying to put in the map
const second_color = d3.scaleQuantize().range(['#ffffcc','#ffeda0','#fed976','#feb24c','#fd8d3c','#fc4e2a','#e31a1c','#b10026']);
const second_svg = d3.select("#chart2")
.append("svg")
.attr("width", second_width)
.attr("height", second_height);
//Load in GeoJSON data
d3.json('https://gist.githubusercontent.com/GerardoFurtado/02aa65e5522104cb692e/raw/8108fbd4103a827e67444381ff594f7df8450411/aust.json')
.then(json => onGeoJsonLoaded(json))
.catch(err => console.log('ERROR: ', err));
const onGeoJsonLoaded = json => {
//Bind data and create one path per GeoJSON feature
const states = second_svg.selectAll('g.state')
.data(json.features)
.enter()
.append('g')
.classed('state', true);
d3.csv('data/Waste_Per_State_Per_Capita(1).csv').then(function(data) {
//The domain of the color scale
second_color.domain([
d3.min(data, function(d) { return d.Total; }),
d3.max(data, function(d) { return d.Total; })
]);
for (var i = 0; i < data.length; i++) {
var data_States = data[i].States;
var dataValue = parseFloat(data[i].Total);
for (var j = 0; j < json.features.length; j++) {
var json_States = json.features[j].properties.STATE_NAME;
if (data_States == json_States) {
json.features[j].properties.value = dataValue;
//Stop looking through the JSON
break;
}
}
}
})
states.append('path')
.attr("d", path)
.attr("stroke", 'white')
.attr("fill", function(d) {
const value = d.properties.value;
console.log(d.properties);
if (value) {
return second_color(value);
} else {
return "#ccc";
}
});
states.append("text")
.attr("fill", "darkslategray")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("text-anchor", "middle")
.attr("dy", 15)
.text(function(d) {
return d.properties.STATE_NAME;
});
d3.json('data/Waste_Per_State_Per_Capita.json')
.then(dataJson => onDataJsonLoaded(dataJson))
.catch(err => console.log('ERR: ', err));
}
const tooltipPath = (width, height, offset, radius) => {
const left = -width / 2;
const right = width / 2;
const top = -offset - height;
const bottom = -offset;
// Creating a polygon for containing data.
return `M 0,0
L ${-offset},${bottom}
H ${left + radius}
Q ${left},${bottom} ${left},${bottom - radius}
V ${top + radius}
Q ${left},${top} ${left + radius},${top}
H ${right - radius}
Q ${right},${top} ${right},${top + radius}
V ${bottom - radius}
Q ${right},${bottom} ${right - radius},${bottom}
H ${offset}
L 0,0 z`;
}
const onDataJsonLoaded = json => {
const rows = Object.keys(json[0]).filter(n => n !== 'States');
const second_tooltip = second_svg.append('g')
.classed('tooltip', true)
.style('visibility', 'hidden');
second_tooltip.append('path')
.attr('d', tooltipPath(200, 80, 5, 5))
rows.forEach((row, index) => {
second_tooltip.append('text')
.text(`${row} :`)
.attr('x', -70)
.attr('y', -68 + index * 18);
second_tooltip.append('text')
.classed(row.replace(' ', '_'), true)
.attr('x', 30)
.attr('y', -68 + index * 18);
second_tooltip.append('text')
.text(`(kg/year)`)
.attr('x', 50)
.attr('y', -68 + index * 18);
});
second_svg.selectAll('g.state')
.on('mouseenter', d => {
const stateData = json.find(s => s.States == d.properties.STATE_NAME);
rows.forEach(row => second_tooltip.select(`.${row.replace(' ', '_')}`).text(stateData[row]));
second_tooltip.attr('transform', `translate(${path.centroid(d)})`);
second_tooltip.style('visibility', 'visible');
})
.on('mouseleave', () => tooltip.style('visibility', 'hidden'));
};
至于我用来加载总值以创建色标的CSV文件,你可以在这里看到:
States,Energy Recovery,Disposal,Recycling,Total
South Australia,36,75,7,118
ACT,53,70,0,123
New South Wales,28,80,48,156
Victoria,51,108,14,173
Tasmania,47,138,0,185
Queensland,50,143,10,203
Northern Territory,34,203,0,237
Western Australia,29,163,29,221
你能告诉我为什么颜色无法加载到等值线中吗?我该如何解决?
谢谢!
- 获取取值范围:
const valueRange = json.reduce((r, s) => r ?
[Math.min(r[0], s.Total), Math.max([1], s.Total)] :
[s.Total, s.Total], null);
- 构建色标:
const color = d3.scaleLinear()
.domain(valueRange)
.range(["#FF0000", "#800000"]);
- 为每个州的路径设置颜色:
svg.selectAll('g.state')
.select('path')
.style('fill', d => {
const stateData = json.find(s => s.State === d.properties.STATE_NAME);
return stateData ? color(stateData.Total) : 'white';
})
const w = 850;
const h = 700;
//Define map projection // geoEqualEarth
const projection = d3.geoMercator()
.center([ 132, -28 ])
.translate([ w/2, h/2 ])
.scale(1000);
//Define path generator
const path = d3.geoPath()
.projection(projection);
//Create SVG
const svg = d3.select("svg")
.attr('width', w)
.attr('height', h)
//Load in GeoJSON data
d3.json('https://gist.githubusercontent.com/GerardoFurtado/02aa65e5522104cb692e/raw/8108fbd4103a827e67444381ff594f7df8450411/aust.json')
.then(json => onGeoJsonLoaded(json))
.catch(err => console.log('GEO ERROR: ', err));
const onGeoJsonLoaded = json => {
//Bind data and create one path per GeoJSON feature
const states = svg.selectAll('g.state')
.data(json.features)
.enter()
.append('g')
.classed('state', true);
states.append('path')
.attr("d", path)
.attr("stroke", 'white');
//.attr("fill", (d, i) => color[i]);
//States
states.append("text")
.attr("fill", "white")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("text-anchor", "middle")
.attr("dy", 15)
.text(function(d) {
return d.properties.STATE_NAME;
});
//Append the name
d3.json('https://api.jsonbin.io/b/60af2dc3d0f4985540524d62')
.then(dataJson => onDataJsonLoaded(dataJson))
.catch(err => console.log('DATA JSON ERR: ', err));
}
const tooltipPath = (width, height, offset, radius) => {
const left = -width / 2
const right = width / 2
const top = -offset - height
const bottom = -offset
return `M 0,0
L ${-offset},${bottom}
H ${left + radius}
Q ${left},${bottom} ${left},${bottom - radius}
V ${top + radius}
Q ${left},${top} ${left + radius},${top}
H ${right - radius}
Q ${right},${top} ${right},${top + radius}
V ${bottom - radius}
Q ${right},${bottom} ${right - radius},${bottom}
H ${offset}
L 0,0 z`
}
const onDataJsonLoaded = json => {
const valueRange = json.reduce((r, s) => r ?
[Math.min(r[0], s.Total), Math.max([1], s.Total)] :
[s.Total, s.Total], null);
const color = d3.scaleLinear()
.domain(valueRange)
.range(["#FF0000", "#800000"]);
const states = svg.selectAll('g.state');
states.select('path')
.style('fill', d => {
const stateData = json.find(s => s.State === d.properties.STATE_NAME);
return stateData ? color(stateData.Total) : 'white';
})
const rows = Object.keys(json[0]).filter(n => n !== 'State');
const tooltip = svg.append('g')
.classed('tooltip', true)
.style('visibility', 'hidden');
tooltip.append('path')
.attr('d', tooltipPath(150, 80, 5, 5))
rows.forEach((row, index) => {
tooltip.append('text')
.text(`${row} :`)
.attr('x', -70)
.attr('y', -68 + index * 18);
tooltip.append('text')
.classed(row.replace(' ', '_'), true)
.attr('x', 40)
.attr('y', -68 + index * 18)
}
);
states
.on('mouseenter', d => {
const stateData = json.find(s => s.State === d.properties.STATE_NAME);
rows.forEach(row => tooltip.select(`.${row.replace(' ', '_')}`).text(stateData[row]));
tooltip.attr('transform', `translate(${path.centroid(d)})`);
tooltip.style('visibility', 'visible');
})
.on('mouseleave', () => tooltip.style('visibility', 'hidden'));
};
.tooltip > path {
fill: white;
stroke: black;
}
.tooltip > text {
font-family: "Ubuntu";
font-size: 12px;
fill: black;
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg/>
我正在 D3 v5 中构建等值线,但是当我尝试加载与红色顺序比例对应的状态颜色时,没有任何反应,所有状态都保持灰色,如下图所示:
这是我用来制作地图的代码:
const second_height = 850;
const projection = d3.geoMercator()
.center([ 132, -28 ])
.translate([ second_width/2, second_height/2 ])
.scale(1000);
const path = d3.geoPath().projection(projection);
//Color scale that I am trying to put in the map
const second_color = d3.scaleQuantize().range(['#ffffcc','#ffeda0','#fed976','#feb24c','#fd8d3c','#fc4e2a','#e31a1c','#b10026']);
const second_svg = d3.select("#chart2")
.append("svg")
.attr("width", second_width)
.attr("height", second_height);
//Load in GeoJSON data
d3.json('https://gist.githubusercontent.com/GerardoFurtado/02aa65e5522104cb692e/raw/8108fbd4103a827e67444381ff594f7df8450411/aust.json')
.then(json => onGeoJsonLoaded(json))
.catch(err => console.log('ERROR: ', err));
const onGeoJsonLoaded = json => {
//Bind data and create one path per GeoJSON feature
const states = second_svg.selectAll('g.state')
.data(json.features)
.enter()
.append('g')
.classed('state', true);
d3.csv('data/Waste_Per_State_Per_Capita(1).csv').then(function(data) {
//The domain of the color scale
second_color.domain([
d3.min(data, function(d) { return d.Total; }),
d3.max(data, function(d) { return d.Total; })
]);
for (var i = 0; i < data.length; i++) {
var data_States = data[i].States;
var dataValue = parseFloat(data[i].Total);
for (var j = 0; j < json.features.length; j++) {
var json_States = json.features[j].properties.STATE_NAME;
if (data_States == json_States) {
json.features[j].properties.value = dataValue;
//Stop looking through the JSON
break;
}
}
}
})
states.append('path')
.attr("d", path)
.attr("stroke", 'white')
.attr("fill", function(d) {
const value = d.properties.value;
console.log(d.properties);
if (value) {
return second_color(value);
} else {
return "#ccc";
}
});
states.append("text")
.attr("fill", "darkslategray")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("text-anchor", "middle")
.attr("dy", 15)
.text(function(d) {
return d.properties.STATE_NAME;
});
d3.json('data/Waste_Per_State_Per_Capita.json')
.then(dataJson => onDataJsonLoaded(dataJson))
.catch(err => console.log('ERR: ', err));
}
const tooltipPath = (width, height, offset, radius) => {
const left = -width / 2;
const right = width / 2;
const top = -offset - height;
const bottom = -offset;
// Creating a polygon for containing data.
return `M 0,0
L ${-offset},${bottom}
H ${left + radius}
Q ${left},${bottom} ${left},${bottom - radius}
V ${top + radius}
Q ${left},${top} ${left + radius},${top}
H ${right - radius}
Q ${right},${top} ${right},${top + radius}
V ${bottom - radius}
Q ${right},${bottom} ${right - radius},${bottom}
H ${offset}
L 0,0 z`;
}
const onDataJsonLoaded = json => {
const rows = Object.keys(json[0]).filter(n => n !== 'States');
const second_tooltip = second_svg.append('g')
.classed('tooltip', true)
.style('visibility', 'hidden');
second_tooltip.append('path')
.attr('d', tooltipPath(200, 80, 5, 5))
rows.forEach((row, index) => {
second_tooltip.append('text')
.text(`${row} :`)
.attr('x', -70)
.attr('y', -68 + index * 18);
second_tooltip.append('text')
.classed(row.replace(' ', '_'), true)
.attr('x', 30)
.attr('y', -68 + index * 18);
second_tooltip.append('text')
.text(`(kg/year)`)
.attr('x', 50)
.attr('y', -68 + index * 18);
});
second_svg.selectAll('g.state')
.on('mouseenter', d => {
const stateData = json.find(s => s.States == d.properties.STATE_NAME);
rows.forEach(row => second_tooltip.select(`.${row.replace(' ', '_')}`).text(stateData[row]));
second_tooltip.attr('transform', `translate(${path.centroid(d)})`);
second_tooltip.style('visibility', 'visible');
})
.on('mouseleave', () => tooltip.style('visibility', 'hidden'));
};
至于我用来加载总值以创建色标的CSV文件,你可以在这里看到:
States,Energy Recovery,Disposal,Recycling,Total
South Australia,36,75,7,118
ACT,53,70,0,123
New South Wales,28,80,48,156
Victoria,51,108,14,173
Tasmania,47,138,0,185
Queensland,50,143,10,203
Northern Territory,34,203,0,237
Western Australia,29,163,29,221
你能告诉我为什么颜色无法加载到等值线中吗?我该如何解决? 谢谢!
- 获取取值范围:
const valueRange = json.reduce((r, s) => r ?
[Math.min(r[0], s.Total), Math.max([1], s.Total)] :
[s.Total, s.Total], null);
- 构建色标:
const color = d3.scaleLinear()
.domain(valueRange)
.range(["#FF0000", "#800000"]);
- 为每个州的路径设置颜色:
svg.selectAll('g.state')
.select('path')
.style('fill', d => {
const stateData = json.find(s => s.State === d.properties.STATE_NAME);
return stateData ? color(stateData.Total) : 'white';
})
const w = 850;
const h = 700;
//Define map projection // geoEqualEarth
const projection = d3.geoMercator()
.center([ 132, -28 ])
.translate([ w/2, h/2 ])
.scale(1000);
//Define path generator
const path = d3.geoPath()
.projection(projection);
//Create SVG
const svg = d3.select("svg")
.attr('width', w)
.attr('height', h)
//Load in GeoJSON data
d3.json('https://gist.githubusercontent.com/GerardoFurtado/02aa65e5522104cb692e/raw/8108fbd4103a827e67444381ff594f7df8450411/aust.json')
.then(json => onGeoJsonLoaded(json))
.catch(err => console.log('GEO ERROR: ', err));
const onGeoJsonLoaded = json => {
//Bind data and create one path per GeoJSON feature
const states = svg.selectAll('g.state')
.data(json.features)
.enter()
.append('g')
.classed('state', true);
states.append('path')
.attr("d", path)
.attr("stroke", 'white');
//.attr("fill", (d, i) => color[i]);
//States
states.append("text")
.attr("fill", "white")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("text-anchor", "middle")
.attr("dy", 15)
.text(function(d) {
return d.properties.STATE_NAME;
});
//Append the name
d3.json('https://api.jsonbin.io/b/60af2dc3d0f4985540524d62')
.then(dataJson => onDataJsonLoaded(dataJson))
.catch(err => console.log('DATA JSON ERR: ', err));
}
const tooltipPath = (width, height, offset, radius) => {
const left = -width / 2
const right = width / 2
const top = -offset - height
const bottom = -offset
return `M 0,0
L ${-offset},${bottom}
H ${left + radius}
Q ${left},${bottom} ${left},${bottom - radius}
V ${top + radius}
Q ${left},${top} ${left + radius},${top}
H ${right - radius}
Q ${right},${top} ${right},${top + radius}
V ${bottom - radius}
Q ${right},${bottom} ${right - radius},${bottom}
H ${offset}
L 0,0 z`
}
const onDataJsonLoaded = json => {
const valueRange = json.reduce((r, s) => r ?
[Math.min(r[0], s.Total), Math.max([1], s.Total)] :
[s.Total, s.Total], null);
const color = d3.scaleLinear()
.domain(valueRange)
.range(["#FF0000", "#800000"]);
const states = svg.selectAll('g.state');
states.select('path')
.style('fill', d => {
const stateData = json.find(s => s.State === d.properties.STATE_NAME);
return stateData ? color(stateData.Total) : 'white';
})
const rows = Object.keys(json[0]).filter(n => n !== 'State');
const tooltip = svg.append('g')
.classed('tooltip', true)
.style('visibility', 'hidden');
tooltip.append('path')
.attr('d', tooltipPath(150, 80, 5, 5))
rows.forEach((row, index) => {
tooltip.append('text')
.text(`${row} :`)
.attr('x', -70)
.attr('y', -68 + index * 18);
tooltip.append('text')
.classed(row.replace(' ', '_'), true)
.attr('x', 40)
.attr('y', -68 + index * 18)
}
);
states
.on('mouseenter', d => {
const stateData = json.find(s => s.State === d.properties.STATE_NAME);
rows.forEach(row => tooltip.select(`.${row.replace(' ', '_')}`).text(stateData[row]));
tooltip.attr('transform', `translate(${path.centroid(d)})`);
tooltip.style('visibility', 'visible');
})
.on('mouseleave', () => tooltip.style('visibility', 'hidden'));
};
.tooltip > path {
fill: white;
stroke: black;
}
.tooltip > text {
font-family: "Ubuntu";
font-size: 12px;
fill: black;
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg/>