Javascript 地图上的d3js部队布局
我试图在地图上放置一个force布局节点系统。一些节点在我使用的json文件中有lon和lat值。其他节点只需要连接,而不需要地理参考。我想将具有lon和lat值的节点以及其他要连接的节点放置到位 (我发现了我遵循的这个示例,但是没有lon和lat值的节点被放置在svg之外:)我还尝试过滤具有lon和lat值的节点,但仍然没有成功 这是我目前得到的: 这是我的密码:Javascript 地图上的d3js部队布局,javascript,html,json,d3.js,geojson,Javascript,Html,Json,D3.js,Geojson,我试图在地图上放置一个force布局节点系统。一些节点在我使用的json文件中有lon和lat值。其他节点只需要连接,而不需要地理参考。我想将具有lon和lat值的节点以及其他要连接的节点放置到位 (我发现了我遵循的这个示例,但是没有lon和lat值的节点被放置在svg之外:)我还尝试过滤具有lon和lat值的节点,但仍然没有成功 这是我目前得到的: 这是我的密码: var w = 1340; var h = 620; //Zoom del mapa porque panamá es muy
var w = 1340;
var h = 620;
//Zoom del mapa porque panamá es muy peque en la aproyección
var zoomOffset = 75000;
var wOffset = 103300;
var hOffset = 11500;
var escala = 0.50;
//Tipo de proyección del mapa escalado y transladado
//posicion del mapa
var projection = d3.geoMercator()
.translate([w + wOffset, h + hOffset])
.scale([zoomOffset])
;
//Los paths que toman el tipo de proyección
var path = d3.geoPath().projection(projection);
//El "centro" del pais
var center = projection([9.018, -79.500])
;
//Esquema de colores
var color = d3.scaleOrdinal(d3.schemeCategory20);
//Define la siulación de fuerza
var fuerza = d3.forceSimulation()
.force("link", d3.forceLink()
.id(function(d){
return d.id;
})
.distance(40))
.force("charge", d3.forceManyBody().strength(-5))
.force("center", d3.forceCenter(w/2, h/2))
;
//Leer datos de ambos json y llamar la funcion que dibuja todo
d3.queue()
.defer(d3.json, 'proyectos_v5.json')
.defer(d3.json, 'panama.json')
.awaitAll(dibujar)
;
//Leer los datos y dibujar los assets y el mapa
function dibujar (error, data){
if (error) {throw error}
//Leer los datos de los json y ponerlos en arreglos distintos
var graph = data[0];
var features = data[1].features;
//Printea los datos para verificar
console.log(graph);
console.log(features);
//Le dice a la simulación cuales son los nodos y los links
fuerza.nodes(graph.nodes);
fuerza.force("link").links(graph.edges);
//svg en donde dibujar
var svg = d3.selectAll("body")
.append("svg")
.attr('width', w)
.attr('height', h)
;
//grupo en donde esten todos los objetos draggeables
var mapa = svg.append("g")
.attr('id', "mapa") //para luego dibujar los circulos y el mapa
//dibuja el mapa, sin zoom porque no se necesita
.selectAll("path")
.data(features)
.enter()
.append("path")
.attr("d", path)
.style('fill', "#EDEDED")
;
//crea las lineas con un svg y los datos de "edges"
var lineas = svg.append('g')
.selectAll("line")
.data(graph.edges)
.enter()
.append("line")
.style("stroke", "black")
.style('stroke-width', 1)
;
//crea los nodos de acuerdo a los nombres
var nodos = svg.append('g')
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.style('fill', function(d, i){
return color(i);
})
.attr('r',5 )
.call(d3.drag()
.on("start", dragInicia)
.on("drag", dragging)
.on("end", dragTermina)) //llama la el metodo de nodos dragg y le dice que hacer en cada momento
;
nodos.append("title")
.text(function(d){
return d.id;
});
//simulación y actualizacion de la posicion de los nodos en cada "tick"
fuerza.on("tick", function (){
lineas
.attr('x1', function(d){
return d.source.x;
})
.attr('y1', function(d){
return d.source.y;
})
.attr('x2', function(d){
return d.target.x;
})
.attr('y2', function(d){
return d.target.y;
})
;
nodos
.attr('cx', function(d){
if(d.fixed== true"){
return projection([d.lon, d.lat])[0];
} else {
return d.x;
}
})
.attr('cy', function(d){
if(d.fixed== "true"){
return projection([d.lon, d.lat])[1];
} else {
return d.y;
}
})
;
})
//crea las funciones para saber qué hacer en cada momento del dragging
function dragInicia(d){
if (!d3.event.active) fuerza.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragging(d){
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragTermina(d){
if(!d3.event.active) fuerza.alphaTarget(0);
d.fx = null;
d.fy = null;
}
};
还有一些json:
{
"id": "Urbanicación La Marina",
"lat": 9.0463,
"lon": -79.4204,
"año": 2019,
"tipo": "proyecto",
"area": "urbano",
"extension": "",
"estado": "",
"publico": "",
"fixed": "true"
},
{
"id": "Zona Logística del aeropuerto de Tocumen",
"lat": 9.0567,
"lon": -79.4191,
"año": 2019,
"tipo": "proyecto",
"area": "urbano",
"extension": "",
"estado": "",
"publico": "",
"fixed": "true"
},
{
"id": "100 ciudades resilentes",
"lat": "",
"lon": "",
"año": "",
"tipo": "actor",
"area": "",
"extension": "",
"estado": "",
"publico": "",
"fixed": "false"
},
{
"id": "ACOBIR",
"lat": "",
"lon": "",
"año": "",
"tipo": "actor",
"area": "",
"extension": "",
"estado": "",
"publico": "",
"fixed": "false"
}
这应该不是问题。但是,到目前为止,您采用的方法将导致一些问题。例如:
.attr('cy', function(d){
if(d.fixed== "true"){
return projection([d.lon, d.lat])[1];
} else {
return d.y;
}
})
这种方法可能会冻结表示节点的圆,但节点会继续在模拟中移动。更新链接时,这肯定会导致视觉问题-它们引用给定节点的模拟位置,而不是其视觉位置。这解释了一些奇怪的链接没有连接到上图中一端的节点
相反,让我们为每个具有纬度和经度的节点设置一个fx
和fy
属性,以便模拟不会更改其位置,例如:
graph.nodes.forEach(function(d) {
if(d.lon && d.lat) {
var p = projection([d.lon,d.lat]);
d.fx = p[0];
d.fy = p[1];
}
})
d.fixed=true
修复v3中的节点,但d.fx
和d.fy
修复v4中的节点,请参见
现在,如果fixed==true,我们可以跳过勾选中的:
.attr('cy', function(d){
return d.y; // d.y == d.fy if d.fy is set
})
现在我们有了固定的节点,但我们应该确保任何取消固定节点的拖动或其他功能都不会取消固定或移动这些投影节点。例如,使用拖动功能:
function dragTermina(d){
if (!d.lon ||!d.lat) { // don't move nodes with geographic data
if(!d3.event.active) force.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
此外,由于可视化是通过地理坐标固定在地面上的,因此我们不需要使用:.force(“center”,d3.forceCenter(w/2,h/2))
将节点居中
综合起来,再加上一些虚构的数据,我得到:
var宽度=960;
var高度=500;
变量图={节点:[
{id:“纽约”,lat:40.706109,lon:-74.01194},
{id:“伦敦”,纬度:51.508070,伦敦:-0.126432},
{id:“蒙得维的亚”,拉丁美洲:-34.901776,拉丁美洲:-56.163983},
{id:“伦敦-纽约1”},
{id:“伦敦-纽约2”},
{id:“伦敦-纽约3”},
{id:“蒙得维的亚伦敦”}
],
链接:[
{来源:“纽约”,目标:“伦敦-纽约1”},
{来源:“纽约”,目标:“伦敦-纽约2”},
{来源:“纽约”,目标:“伦敦-纽约3”},
{来源:“伦敦-纽约1”,目标:“伦敦”},
{来源:“伦敦-纽约2”,目标:“伦敦”},
{来源:“伦敦-纽约3”,目标:“伦敦”},
{来源:“伦敦”,目标:“蒙得维的亚伦敦”},
{来源:“蒙得维的亚伦敦”,目标:“蒙得维的亚”}
]
}
var force=d3.forceSimulation()
.force(“链接”,d3.forceLink()
.id(功能(d){
返回d.id;
})
.距离(10))
.力(“电荷”,d3.力人体().力(-200));
var svg=d3.选择(“主体”)
.append(“svg”)
.attr(“宽度”,宽度)
.attr(“高度”,高度);
var projection=d3.geoMercator()
.center([0,10])
.翻译([宽度/2,高度/2]);
var path=d3.geoPath().projection(projection);
var g=svg.append(“g”);
d3.json(“https://unpkg.com/world-atlas@1/world/110m.json”)。然后(函数(数据){
g、 选择全部(“路径”)
.data(topojson.object(data,data.objects.countries).geometrics)
.输入()
.append(“路径”)
.attr(“d”,路径)
.attr(“填充”、“浅绿色”);
var links=svg.append('g')
.selectAll(“行”)
.数据(图表.链接)
.输入()
.附加(“行”)
.attr(“笔划宽度”,2)
.attr(“笔划”、“黑色”);
var nodes=svg.append('g')
.selectAll(“圆圈”)
.数据(图.节点)
.输入()
.附加(“圆圈”)
.attr('r',5)
.call(d3.drag()
.on(“开始”,德拉吉尼亚)
.打开(“拖动”,拖动)
.在(“结束”,dragTermina));
力节点(图节点);
力。力(“链接”)。链接(图。链接);
graph.nodes.forEach(函数(d){
如果(d.lon&d.lat){
var p=投影([d.lon,d.lat]);
d、 fx=p[0];
d、 fy=p[1];
}
})
//“滴答声”中节点位置的模拟和实现
强制打开(“勾号”,函数(){
链接
.attr('x1',函数(d){
返回d.source.x;
})
.attr('y1',函数(d){
返回d.source.y;
})
.attr('x2',函数(d){
返回d.target.x;
})
.attr('y2',函数(d){
返回d.target.y;
})
;
节点
.attr('cx',函数(d){
返回d.x;
})
.attr('cy',函数(d){
返回d.y;
})
;
})
功能性阴道病(d){
如果(!d.lon | |!d.lat){
如果(!d3.event.active)force.alphaTarget(0.3.restart();
d、 fx=d.x;
d、 fy=d.y;
}
}
函数拖动(d){
如果(!d.lon | |!d.lat){
d、 fx=d3.event.x;
d、 fy=d3.event.y;
}
}
功能绘图终端(d){
如果(!d.lon | |!d.lat){
如果(!d3.event.active)力α目标(0);
d、 fx=null;
d、 fy=null;
}
}
});
你能分享几行你的数据吗(包括有和没有lat/long的两点)?是的,我用一段json更新了这个问题!在已知节点周围单独画一个圆,然后将相关节点放在圆上;如果没有坐标,它们可能就不受限制了……我可以通过在.on(“click”函数:graph.nodes.forEach(函数(d){if(d.fixed==“true”){var pos=projection([d.lon,d.lat])d.x=pos[0]d.y=pos[1]}上写下它来解决部分问题{d.x=d.x;d.y=d.y;}