[英]How to drag and rotate orthographic map (globe) using d3.js
設置:我正在構建一個地球應用程序,以更好地直觀地表示世界各地區的數據。 它是使用 topojson 構建 d3.js 來構建幾何的。
我目前正在實施 ivyywang here實現的阻力。 (不要迷失在數學函數中,除非你是狀態:“數學書呆子大師”)
我的項目目前在這里。
問題:我按正字法投影了地球,並成功實現了拖動功能......除了。 只要我的光標在一個國家/地區的邊界內,我就只能單擊並拖動地球儀。 如何投影我的 SVG 以便整個畫布響應我的拖動事件?
相關代碼:
首先,我從 MySQL 請求中獲取一些數據並將其存儲在 countryStattistics 中。 我通過以下函數運行它以更好地索引它。
var countryStatistics = (returned from mySQL query)
//this function build dataById[] setting data keyed to idTopo
function keyIdToData(d){
countryStatistics.forEach(function(d) {
dataById[d.idTopo] = d;
});
}
function visualize(statisticalData, mapType){
//pass arguments each function call to decide what data to viasually display, and what map type to use
var margin = {top: 100, left: 100, right: 100, bottom:100},
height = 800 - margin.top - margin.bottom,
width = 1200 - margin.left - margin.right;
//a simple color scale to correlate to data
var colorScale = d3.scaleLinear()
.domain([0, 100])
.range(["#646464", "#ffff00"])
//create svg
var svg = d3.select("#map")
.append("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.left + margin.right)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
//here I attmpt to fill the svg with a different color. but it is unresponsive
.attr("fill", "blue")
正如您在此代碼塊的末尾所看到的,我在 SVG 元素上調用了 .attr("fill"... 但無法獲得要渲染的顏色。也許這與我的光標在此空間中沒有響應的原因有關。
繼續……
//set projection type to 2D map or 3d globe dependinding on argument passed see function below
var projection = setMapType(mapType, width, height);
//a function to call on visualize() to set projection type for map style.
function setMapType(mapType, width, height) {
if(mapType === "mercator") {
let projection = d3.geoMercator()
.translate([ width / 2, height / 2 ])
.scale(180)
return projection;
}else if (mapType === "orthographic"){
let projection = d3.geoOrthographic()
.clipAngle(90)
.scale(240);
return projection;
}
//pass path lines to projections
var path = d3.geoPath()
.projection(projection);
//here I create and call the drag function only when globe projection is displayed elected
if(mapType == "orthographic"){
var drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged);
svg.call(drag);
}
//coordinate variables
var gpos0,
o0;
function dragstarted(){
gpos0 = projection.invert(d3.mouse(this));
o0 = projection.rotate();
}
function dragged(){
var gpos1 = projection.invert(d3.mouse(this));
o0 = projection.rotate();
var o1 = eulerAngles(gpos0, gpos1, o0);
projection.rotate(o1);
svg.selectAll("path").attr("d", path);
}
//load in the topojson file
d3.queue()
.defer(d3.json, "world110m.json")
.await(ready)
上述函數指的是計算正交旋轉所需的數學函數。 您可以在 ivyywang 的代碼塊中在第一個鏈接的頂部看到它們。
function ready (error, data){
if (error) throw error;
//output data to see what is happening
console.log("topojson data: ")
console.log(data);
//I suspect there may be an issue with this code.
countries = topojson.feature(data, data.objects.countries)
//bind dataById data into countries topojson variable
.features.map(function(d) {
d.properties = dataById[d.id];
return d
});
console.log("countries:")
console.log(countries)
我懷疑上面的國家變量可能是罪魁禍首,主要是因為我不完全理解這段代碼。 在這個變量中,我將keyIdToData()
處理的 countryStatistics 數據綁定為嵌套對象“屬性”與我的 topojson 數據。 我控制台記錄它以查看數據。
svg.selectAll(".country")
.data(countries)
.enter().append("path")
.attr("class", "country")
.attr("d", path)
//make fill gradient depend on data
.attr("fill", function(countries){
//if no data, country is grey
if(countries.properties == undefined){
return "rgb(100 100 100)";
}
//else pass data to colorScale()
return colorScale(countries.properties.literacy)
})
.on('mouseover', function(d) {
//on hover set class hovered which simply changes color with a transition time
d3.select(this).classed("hovered", true)
})
.on('mouseout', function(d) {
d3.select(this).classed("hovered", false)
})
}
};
最后我們有了這個小功能
//this function build dataById[] setting data keyed to idTopo
function keyIdToData(d){
countryStatistics.forEach(function(d) {
dataById[d.idTopo] = d;
});
}
可能性:似乎我的 SVG 渲染不包括我的空(非國家)區域。 我的 SVG 結構可能有問題嗎? 或者當我更改數據並將我的 dataById 附加到我的 topojson 時,我可能會干擾 SVG 構造?
感謝您將其制作成這么遠的鍵盤忍者。 有任何想法嗎?
問題
您的鼠標交互僅在父g
內繪制路徑的位置,而不是它們之間的空間。 您應用於g
的填充會被您應用於子路徑的樣式覆蓋。
查看您擁有的內容,您的變量svg
包含一個g
:
//create svg
var svg = d3.select("#map")
.append("svg")
...
.append("g") // return a newly created and selected g
...
.attr("fill", "blue") // returns same g
對於鼠標交互, g
只能與其中存在的元素進行交互。 對於g
, fill
屬性不會直接做任何事情,它只應用於演示元素(和動畫):
作為表示屬性,它 [fill] 可以應用於任何元素,但它只對以下十一個元素有效:
<altGlyph>
、<circle>
、<ellipse>
、<path>
、<polygon>
、<polyline>
、<rect>
、<text>
、<textPath>
、<tref>
和<tspan>
(MDN )
在g
上使用fill
代替着色子元素,您的路徑。 雖然你直接給它們上色,所以藍色沒有視覺效果:
var g = d3.select("body") .append("svg") .append("g") .attr("fill","orange"); // Inherit fill: g.append("rect") .attr("width",50) .attr("height",50) // Override inheritable fill: g.append("rect") .attr("x", 100) .attr("width",50) .attr("height",50) .attr("fill","steelblue");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
解決方案
您需要創建一個要與之交互的元素,以便在當前沒有路徑的地方進行拖動。
現在,我不認為你想讓整個 svg 背景變成藍色,只是地球上不屬於一個國家的部分。 您可以使用 geojson 球體來做到這一點。 從技術上講,d3 不是 geojson 規范的一部分,它識別為覆蓋整個星球的 geojson 類型(因此它不需要坐標)。 在將國家/地區添加到地球之前,添加一個球體,這為拖動事件提供了與之交互的元素:
svg.append("path")
.attr("d", path({type:"Sphere"})
.attr("fill","blue");
這填充了海洋(和陸地),我們可以在其上附加國家/地區。 現在因為球體和國家都是同一個g
一部分,我們可以像現在一樣在整個地球上實現拖動,但是現在沒有鼠標交互不起作用的漏洞。
這是一個帶有正交投影和最基本的拖動功能的快速演示:
var svg = d3.select("svg").append("g"); var projection = d3.geoOrthographic() .translate([250,250]) var path = d3.geoPath().projection(projection); d3.json("https://unpkg.com/world-atlas@1/world/110m.json").then( function(data) { var world = {type:"Sphere"} svg.append("path") .datum(world) .attr("d", path) .attr("fill","lightblue"); svg.selectAll(null) .data(topojson.feature(data,data.objects.land).features) .enter() .append("path") .attr("fill","lightgreen") .attr("d",path); svg.call(d3.drag() .on("drag", function() { var xy = d3.mouse(this); projection.rotate(xy) svg.selectAll("path") .attr("d",path); })) });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <script src="https://unpkg.com/topojson-client@3"></script> <svg width="500" height="500"></svg>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.