JSON数据无法通过本地访问!!!换成Web服务器之后又说什么跨域访问,我真的太菜了什么都不懂。
通过Python搭建简单Web服务器
最后:这个人真的是太赞了!!!
getJSON无法读取本地json数据的问题
我用的是HBilder,所以设置了一个内置web服务器就可以访问了
path.countries {
stroke-width: 1;
stroke: #75739F;
fill: #5EAFC6;
}
circle.cities {
stroke-width: 1;
stroke: #4F442B;
fill: #FCBC34;
}
circle.centroid {
fill: #75739F;
pointer-events: none;
}
rect.bbox {
fill: none;
stroke-dasharray: 5 5;
stroke: #75739F;
stroke-width: 2;
pointer-events: none;
}
path.graticule {
fill: none;
stroke-width: 1;
stroke: #9A8B7A;
}
path.graticule.outline {
stroke: #9A8B7A;
}
path.merged {
fill: #9A8B7A;
stroke: #4F442B;
stroke-width: 2px;
}
官方API:不同的projection
Mercator Projection
代码:
d3.json("../data/world.geojson", createMap);
function createMap(countries) {
var aProjection = d3.geoMercator();
var geoPath = d3.geoPath().projection(aProjection);
//d3.geoPath()默认是albersUSA,是一种只适合USA地图的投影
d3.select("svg").selectAll("path").data(countries.features)
.enter()
.append("path")
.attr("d", geoPath)
.attr("class", "countries");
}
在不同的投影上,有不同的技巧来确定合适的比例可以展示整个地图。
比如说对于Mercator Projection:将宽度(width)除以2,然后将商除以π;找到一个合适的比例需要经系列的实验,但是这件事器在有了zoom之后会好办很多。
var aProjection = d3.geoMercator()
.scale(80)
.translate([250, 250]);
var PromiseWrapper = (xhr, d) => new Promise(resolve => xhr(d, (p) => resolve(p)));
Promise
.all([
PromiseWrapper(d3.json, "../data/world.geojson"),
PromiseWrapper(d3.csv, "../data/cities.csv")
])
.then(resolve => {
createMap(resolve[0], resolve[1]);
});
function createMap(countries, cities) {
var projection = d3.geoMercator()
.scale(80)
.translate([250, 250]);
var geoPath = d3.geoPath().projection(projection);
d3.select("svg").selectAll("path").data(countries.features)
.enter()
.append("path")
.attr("class", "countries")
.attr("d", geoPath);
d3.select("svg").selectAll("circle").data(cities)
.enter()
.append("circle")
.attr("class", "cities")
.attr("r", 3)
.attr("cx", d => projection([d.x,d.y])[0])
.attr("cy", d => projection([d.x,d.y])[1]);
}
Mollweide Projection:equal-area projection
function createMap(countries, cities) {
var projection = d3.geoMollweide()
.scale(120)
.translate([250, 250]);
var geoPath = d3.geoPath().projection(projection);
var featureSize = d3.extent(countries.features, d => geoPath.area(d))
var countryColor = d3.scaleQuantize()
.domain(featureSize).range(colorbrewer.Reds[7]);
d3.select("svg").selectAll("path").data(countries.features)
.enter()
.append("path")
.attr("d", geoPath)
.attr("class", "countries")
.style("fill", d => countryColor(geoPath.area(d)))
.style("stroke", d => d3.rgb(countryColor(geoPath.area(d))).darker());
d3.select("svg").selectAll("circle").data(cities)
.enter()
.append("circle")
.attr("class", "cities")
.attr("r", 3)
.attr("cx", d => projection([d.x,d.y])[0])
.attr("cy", d => projection([d.x,d.y])[1]);
}
计算某个区域的边界以及中心
function centerBounds(d) {
var thisBounds = geoPath.bounds(d);
var thisCenter = geoPath.centroid(d);//获得面积中心
console.log(thisBounds);
d3.select("svg")
.append("rect")
.attr("class", "bbox")
.attr("x", thisBounds[0][0]) //Bounds[0]表示最左边界
.attr("y", thisBounds[0][1]) //Bounds[1]表示最右边界
.attr("width", thisBounds[1][0] - thisBounds[0][0])
.attr("height", thisBounds[1][1] - thisBounds[0][1]);
d3.select("svg")
.append("circle")
.attr("class", "centroid")
.attr("r", 5)
.attr("cx", thisCenter[0]).attr("cy", thisCenter[1]);
}
function clearCenterBounds() {
d3.selectAll("circle.centroid").remove();
d3.selectAll("rect.bbox").remove();
}
var graticule = d3.geoGraticule();
d3.select("svg").insert("path", "path.countries")
.datum(graticule)
.attr("class", "graticule line")
.attr("d", geoPath);
d3.select("svg").insert("path", "path.countries")
.datum(graticule.outline)
.attr("class", "graticule outline")
.attr("d", geoPath);
var mapZoom = d3.zoom()
.on("zoom", zoomed);
var zoomSettings = d3.zoomIdentity
.translate(250, 250)
.scale(120);
d3.select("svg").call(mapZoom).call(mapZoom.transform, zoomSettings);
function zoomed() {
var e = d3.event;
projection.translate([e.transform.x, e.transform.y])
.scale(e.transform.k);//为projection设置新的位置以及比例
d3.selectAll("path.graticule").attr("d", geoPath);
d3.selectAll("path.countries").attr("d", geoPath);
d3.selectAll("circle.cities")
.attr("cx", d => projection([d.x,d.y])[0])
.attr("cy", d => projection([d.x,d.y])[1]);
}
为了避免有一些人不知道双击,拖拽以及鼠标滚轮这些交互方式,生成button进行缩放
function zoomButton(zoomDirection) {
var width = 500;
var height = 500;
//需要计算x,y以及中心
if (zoomDirection == "in") {
var newZoom = projection.scale() * 1.5;
var newX = ((projection.translate()[0] - (width / 2)) * 1.5) + width / 2;
var newY = ((projection.translate()[1] - (height / 2)) * 1.5) + height / 2;
}
else if (zoomDirection == "out") {
var newZoom = projection.scale() * .75;
var newX = ((projection.translate()[0] - (width / 2)) * .75) + width / 2;
var newY = ((projection.translate()[1] - (height / 2)) * .75) + height / 2;
}
var newZoomSettings = d3.zoomIdentity
.translate(newX, newY)
.scale(newZoom);
d3.select("svg").transition().duration(500).call(mapZoom.transform, newZoomSettings);
}
d3.select("#controls").append("button")
.on("click", () => { zoomButton("in")}).html("Zoom In");
d3.select("#controls").append("button")
.on("click", () => { zoomButton("out")}).html("Zoom Out");
不需要太麻烦,使用一个特殊的投影即可:orthographic projection
var projection = d3.geoOrthographic()
.scale(200)
.translate([250, 250])
.center([0,0]);
为了使他可以旋转起来,则需要隐藏那些不在当前视图的点
d3.selectAll("circle.cities")
.each(function (d, i) {
var projectedPoint = projection([d.x,d.y]);
var x = parseInt(d.x);
var display = x + currentRotate < 90 && x + currentRotate > -90
|| (x + currentRotate < -270 && x + currentRotate > -450)
|| (x + currentRotate > 270 && x + currentRotate < 450)
? "block" : "none";
d3.select(this)
.attr("cx", projectedPoint[0])
.attr("cy", projectedPoint[1])
.style("display", display);
})
问题:在这里使用的featureSize的大小是根据显示在地球仪上的大小(但是真实世界不会因为地球仪的旋转,面积而发生变化)
var geoPath = d3.geoPath().projection(projection);
var featureSize = d3.extent(countries.features, d => geoPath.area(d));
var countryColor = d3.scaleQuantize()
.domain(featureSize).range(colorbrewer.Reds[7]);
解决方法:使用d3.geoArea(),countries coloured by their geographic area, rather than their graphical area.
var realFeatureSize = d3.extent(countries.features, d => d3.geoArea(d));
var newFeatureColor = d3.scaleQuantize()
.domain(realFeatureSize)
.range(colorbrewer.Reds[7]);
更复杂巧妙的拖拽,可以查看bl.ocks.org
样例地址:http://bl.ocks.org/emeeks/10173187
Tilt is the angle of the perspective on the data, whereas distance is the percentage of the radius of the earth.
github地址:TopoJSON
在使用TopoJSON之前,需要包含TopoJSON.js库。
var PromiseWrapper = (xhr, d) => new Promise(resolve => xhr(d, (p) => resolve(p)));
Promise
.all([
PromiseWrapper(d3.json, "../data/world.topojson"),
PromiseWrapper(d3.csv, "../data/cities.csv")
])
.then(resolve => {
createMap(resolve[0], resolve[1]);
});
function createMap(topoCountries) {
var countries =
topojson.feature(topoCountries, topoCountries.objects.countries);
}
可以合并边界线
mergeAt(0);
//这里的0表示 小于或者大于 0°精度和维度
function mergeAt(mergePoint) {
var filteredCountries = topoCountries.objects.countries.geometries
.filter(d => {
var thisCenter = d3.geoCentroid(
topojson.feature(topoCountries, d) );
return thisCenter[1] > mergePoint? true : null;
});
d3.select("svg").append("g")
.datum(topojson.merge(topoCountries, filteredCountries))
.insert("path")
.attr("class", "merged")
.attr("d", geoPath);
}
Topojson提供了另一个函数:Topojson.mergeArcs,可以让你合并形状,并且保持返回值依旧为TopoJSON格式。
函数 Topojson.neighbors返回共享边界的所有features的数组
var neighbors =
topojson.neighbors(topoCountries.objects.countries.geometries);
// 每块根据i有一个编号
d3.selectAll("path.countries")
.on("mouseover", findNeighbors)
.on("mouseout", clearNeighbors);
function findNeighbors (d,i) {
d3.select(this).style("fill", "#FE9922");
d3.selectAll("path.countries")
.filter((p,q) => neighbors[i].indexOf(q) > -1).style("fill", "#41A368");
}
//p表示d,q表示i
function clearNeighbors () {
d3.selectAll("path.countries").style("fill", "#C4B9AC");
}
Tile-based maps:https://docs.mapbox.com/mapbox-gl-js/api/
The tile data used in tile maps:https://observablehq.com/@d3/satellite
Hexbins:https://observablehq.com/@d3/hexbin-map
使用d3.geom.voronoi函数 从类似的点导出多边形:https://www.jasondavies.com/maps/voronoi/us-capitals/
Cartograms:比较统计地图:
https://www.jasondavies.com/maps/dorling-world/
https://bl.ocks.org/mbostock/4055908
给地图提供上下左右的方向键,按相应的键,地图往哪边移;
但是实际上,地图的移动方向是正好与键的方向相反,点右键,地图的起始点(x,y)得向左上走;
如果有zooming(x, y, k),drag就可以省去了,因为zooming包含了drag.
//Define what to do when panning or zooming
var zooming = function(d) {
//Log out d3.event.transform, so you can see all the goodies inside
//console.log(d3.event.transform);
//New offset array
var offset = [d3.event.transform.x, d3.event.transform.y];
//Calculate new scale
var newScale = d3.event.transform.k * 2000;
//Update projection with new offset and scale
projection.translate(offset)
.scale(newScale);
//Update all paths and circles
svg.selectAll("path")
.attr("d", path);
svg.selectAll("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
});
}
//Then define the zoom behavior
var zoom = d3.zoom()
.on("zoom", zooming);
//The center of the country, roughly
var center = projection([-97.0, 39.0]);
//Create a container in which all zoom-able elements will live
var map = svg.append("g")
.attr("id", "map")
.call(zoom) //Bind the zoom behavior
.call(zoom.transform, d3.zoomIdentity //Then apply the initial transform
.translate(w/2, h/2)
.scale(0.25)
.translate(-center[0], -center[1]));
d3.selectAll(".pan")
.on("click", function() {
//Set how much to move on each click
var moveAmount = 50;
//Set x/y to zero for now
var x = 0;
var y = 0;
//Which way are we headed?
var direction = d3.select(this).attr("id");
//Modify the offset, depending on the direction
switch (direction) {
case "north":
y += moveAmount; //Increase y offset
break;
case "south":
y -= moveAmount; //Decrease y offset
break;
case "west":
x += moveAmount; //Increase x offset
break;
case "east":
x -= moveAmount; //Decrease x offset
break;
default:
break;
}
//This triggers a zoom event, translating by x, y
map.transition()
.call(zoom.translateBy, x, y);
});
制作地图过程:
1. Find shapefiles
2. Choose a resolution
3. Simplify the shape
4. Convert to GeoJSON
5. Choose a projection