我有一張地圖,上面有一些重疊點。我正在使用四叉樹和 turf.js 來確定,當我單擊一個點時,30 英里半徑內還有多少其他點。
我想要做的(如果該半徑中有多個點)是使用 d3.forceSimulation 來均勻分布重疊點。
這是我想要做的一個非常接近的例子,但使用 d3v3 和谷歌地圖:
非常感謝幫助!!
let margin = { top: 0, right: 0, bottom: 10, left: 0 },
width = 1000 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
let projection = geoAlbersUsaTerritories.geoAlbersUsaTerritories()
.scale(width)
.translate([width / 2, height / 2.2]);
const path = d3.geoPath()
.projection(projection);
var simulation = d3.forceSimulation()
.force('charge', d3.forceManyBody().strength(-160))
.stop()
let eventX,
eventY
const formatDate2 = d3.timeFormat("%m-%Y")
const svg = d3.select("#content")
.append("svg")
.attr('id', 'map')
.style("width", width margin.left margin.right)
.style("height", height margin.top margin.bottom)
const map = svg.append('g')
.attr("transform", "translate(" margin.left "," margin.top ")")
.attr('class', 'map')
const landmass = map.append('g').attr('class', 'land')
const places = map.append('g').attr('id', 'places')
svg.append('ellipse').classed("radius", true).attr('id', 'locate')
d3.json("https://unpkg.com/[email protected]/states-10m.json").then(function (usa) {
landmass.selectAll('path')
.data(topojson.feature(usa, usa.objects.nation).features)
.enter().append("path")
.attr("d", path)
.attr("class", "outline")
.attr('fill', '#ccc')
.attr("stroke", "#999")
landmass.append("path")
.datum(topojson.mesh(usa, usa.objects.states, function (a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path)
.attr('fill', 'none')
.attr('stroke', 'white')
});
const data = [
{
"id": 3448,
"name": "General Edward Lawrence Logan Intl",
"city": "Boston",
"faa": "BOS",
"latitude": 42.364347,
"longitude": -71.005181
},
{
"id": 3453,
"name": "Metropolitan Oakland Intl",
"city": "Oakland",
"faa": "OAK",
"latitude": 37.721278,
"longitude": -122.220722
},
{
"id": 3454,
"name": "Eppley Afld",
"city": "Omaha",
"faa": "OMA",
"latitude": 41.303167,
"longitude": -95.894069
},
{
"id": 3457,
"name": "Wichita Mid Continent",
"city": "Wichita",
"faa": "ICT",
"latitude": 37.649944,
"longitude": -97.433056
},
{
"id": 3458,
"name": "Kansas City Intl",
"city": "Kansas City",
"faa": "MCI",
"latitude": 39.297606,
"longitude": -94.713905
},
{
"id": 3459,
"name": "Dane Co Rgnl Truax Fld",
"city": "Madison",
"faa": "MSN",
"latitude": 43.139858,
"longitude": -89.337514
},
{
"id": 3462,
"name": "Phoenix Sky Harbor Intl",
"city": "Phoenix",
"faa": "PHX",
"latitude": 33.434278,
"longitude": -112.011583
},
{
"id": 3467,
"name": "Spokane Intl",
"city": "Spokane",
"faa": "GEG",
"latitude": 47.619861,
"longitude": -117.533833
},
{
"id": 3469,
"name": "San Francisco Intl",
"city": "San Francisco",
"faa": "SFO",
"latitude": 37.618972,
"longitude": -122.374889
},
{
"id": 3472,
"name": "Gainesville Rgnl",
"city": "Gainesville",
"faa": "GNV",
"latitude": 29.690056,
"longitude": -82.271778
},
{
"id": 3473,
"name": "Memphis Intl",
"city": "Memphis",
"faa": "MEM",
"latitude": 35.042417,
"longitude": -89.976667
},
{
"id": 3484,
"name": "Los Angeles Intl",
"city": "Los Angeles",
"faa": "LAX",
"latitude": 33.942536,
"longitude": -118.408075
},
{
"id": 3486,
"name": "Cleveland Hopkins Intl",
"city": "Cleveland",
"faa": "CLE",
"latitude": 41.411689,
"longitude": -81.849794
},
{
"id": 3494,
"name": "Newark Liberty Intl",
"city": "Newark",
"faa": "EWR",
"latitude": 40.6925,
"longitude": -74.168667
},
{
"id": 3502,
"name": "Dallas Love Fld",
"city": "Dallas",
"faa": "DAL",
"latitude": 32.847111,
"longitude": -96.851778
},
{
"id": 3550,
"name": "George Bush Intercontinental",
"city": "Houston",
"faa": "IAH",
"latitude": 29.984433,
"longitude": -95.341442
},
{
"id": 3559,
"name": "El Paso Intl",
"city": "El Paso",
"faa": "ELP",
"latitude": 31.80725,
"longitude": -106.377583
},
{
"id": 3566,
"name": "William P Hobby",
"city": "Houston",
"faa": "HOU",
"latitude": 29.645419,
"longitude": -95.278889
},
{
"id": 3570,
"name": "Pittsburgh Intl",
"city": "Pittsburgh",
"faa": "PIT",
"latitude": 40.491467,
"longitude": -80.232872
},
{
"id": 3576,
"name": "Miami Intl",
"city": "Miami",
"faa": "MIA",
"latitude": 25.79325,
"longitude": -80.290556
},
{
"id": 3582,
"name": "Long Beach",
"city": "Long Beach",
"faa": "LGB",
"latitude": 33.817722,
"longitude": -118.151611
},
{
"id": 3585,
"name": "Indianapolis Intl",
"city": "Indianapolis",
"faa": "IND",
"latitude": 39.717331,
"longitude": -86.294383
},
{
"id": 3589,
"name": "Westchester Co",
"city": "White Plains",
"faa": "HPN",
"latitude": 41.066959,
"longitude": -73.707575
},
{
"id": 3697,
"name": "La Guardia",
"city": "New York",
"faa": "LGA",
"latitude": 40.777245,
"longitude": -73.872608
},
{
"id": 3747,
"name": "Chicago Midway Intl",
"city": "Chicago",
"faa": "MDW",
"latitude": 41.785972,
"longitude": -87.752417
},
{
"id": 3797,
"name": "John F Kennedy Intl",
"city": "New York",
"faa": "JFK",
"latitude": 40.639751,
"longitude": -73.778925
},
{
"id": 3830,
"name": "Chicago Ohare Intl",
"city": "Chicago",
"faa": "ORD",
"latitude": 41.978603,
"longitude": -87.904842
}
]
d3.selectAll('.close').on('click', function () {
d3.selectAll('.popup').remove()
})
data.forEach(function (d) {
d.latitude = d.latitude;
d.longitude = d.longitude;
})
d3.selectAll('.location').remove()
let locations = places.selectAll(".location")
.data(data);
locations.enter()
.append("circle")
.attr('id', d => 'n' d.id)
.attr("class", 'location')
.attr('cx', d => projection([d.longitude, d.latitude])[0])
.attr('cy', d => projection([d.longitude, d.latitude])[1])
.attr("r", 5)
.attr('fill', 'green')
.style('stroke', '#fff')
.style('stroke-width', .5)
.style("opacity", .75)
.on('click', function (event, d) {
simulation.stop()
var isSelectedCode = d.detention_facility_code
var isSelectedName = d.name
var whichclass = d3.select(this).attr("class").split(' ');
let activeIndex = whichclass.indexOf('active')
var sel = d3.select(this);
sel.raise();
let latlng = [d.longitude, d.latitude]
$('#clickedFacility').text(d.name)
$('#slider').removeClass('hide')
showRadius(latlng, far)
})
.on("mouseover", function (event, d) {
var sel = d3.select(this);
sel.raise();
let tooltip_str = d.name
tooltip.html(tooltip_str)
.style("visibility", "visible");
})
.on("mousemove", function (event, d) {
tooltip.style("top", event.pageY - (tooltip.node().clientHeight 5) "px")
.style("left", event.pageX - (tooltip.node().clientWidth / 2.0) "px");
})
.on("mouseout", function (event, d) {
var sel = d3.select(this);
sel.lower();
tooltip.style("visibility", "hidden");
})
locations
.transition()
.duration(100)
.attr("class", d => "location " d.name.replace(/[\s]/g, '') ' ' d.type_detailed.replace(/\s |[,\/]/g, "") ' closed' d.is_closed)
.attr("r", 5)
.attr('fill', 'green')
locations.exit()
.remove();
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip");
var info = svg.append("div")
.attr("class", "info");
const quadtree = d3.quadtree()
.x(d => d.longitude)
.y(d => d.latitude)
.addAll(data);
let miles = 30
let far = (1 / 60) * miles; // in degrees
function showRadius(evt, far) {
d3.select('ellipse.radius').remove();
let radiusCircle = map.append("ellipse").classed("radius", true).attr('id', 'locate')
let xy = projection.invert(evt)
console.log('xy', xy)
let xyObject = { "longitude": evt[0], "latitude": evt[1] }
radiusLng = evt[0] far
radiusLat = evt[1] far
radiusLngLat = [ radiusLng, radiusLat]
radiusPoint = projection(radiusLngLat)
console.log('radiusPoint', radiusPoint)
radiusX = Math.abs(evt[1] far)
radiusY = Math.abs(evt[0] far)
radiusToPoint = projection([radiusY, radiusX][0])
d3.select('ellipse.radius').classed('hide', false)
d3.select('ellipse.radius').classed('show', true)
let radiusprojx = projection(evt)[0]
let radiusprojxN = projection(evt)[0] - eventX
let radiusprojy = projection(evt)[1]
let radiusprojyN = projection(evt)[1] - eventY
radiusCircle
.attr('cx', d => (projection(evt)[0]))
.attr('cy', d => (projection(evt)[1]))
.attr('rx', 20)
.attr('ry', 20)
let hits = [];
quadtree.visit(nearest(xyObject, far, hits))
for (i = 0; i < hits.length; i ) {
let line = turf.lineString([[evt[1], evt[0]], [hits[i].latitude, hits[i].longitude]]);
let length = turf.length(line, { units: 'miles' });
hits[i].distance = length.toFixed(2) ' miles';
}
hits.sort(function (a, b) { return d3.ascending(a.distance, b.distance) })
let locationsInRadius = hits.map(a => a.id);
console.log('locationsInRadius', locationsInRadius)
d3.selectAll('.location').attr('fill', 'green')
locationsInRadius.forEach(function (d, i) {
d3.select('#n' d).attr('fill', 'blue')
})
let total_count = hits.length
$("#hitnumber").text(total_count " airports within 30 miles")
d3.selectAll('.list-item').remove()
let listItem = d3.selectAll('#hits').selectAll('text')
.data(hits)
.attr('padding-left', '20px')
.enter().append('div').attr('class', 'list-item')
.html(d => d.name "<br/>Lat: " d.latitude "<br/>Lng: " d.longitude "<br/>Distance: " d.distance)
if (hits.length == 1) {
} else {
const hitids = []
for (i = 0; i < hits.length; i ) {
hitids.push('#n' hits[i].id)
}
let idstoget = hitids.toString()
let forceids = d3.selectAll(idstoget)
simulation.force('x', d3.forceX().strength(10).x(radiusPoint[0]))
simulation.force('y', d3.forceY().strength(10).y(radiusPoint[1]))
// simulation.force('center', d3.forceCenter(radiusPoint[0], radiusPoint[1]))
simulation.alpha(1).restart()
simulation.nodes(forceids)
.on('tick', ticked)
function ticked() {
update(forceids)
}
function update(forceids) {
forceids
.attr('cx', function (d) { return d.x })
.attr('cy', function (d) { return d.y })
}
}
}
function nearest(node, radius, hits) {
if (!hits) hits = [];
var r = radius,
nx1 = node.longitude - r,
nx2 = node.longitude r,
ny1 = node.latitude - r,
ny2 = node.latitude r;
return function (quad, x1, y1, x2, y2) {
if (quad.data && (quad.data !== node)) {
var x = node.longitude - quad.data.longitude,
y = node.latitude - quad.data.latitude,
l = Math.sqrt(x * x y * y),
r = radius;
if (l < r) {
hits.push(quad.data)
} else {
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
}
}
.tooltip {
position: absolute;
padding: 15px;
font: 12px sans-serif;
background: #fff;
color: #000;
border: 0px;
pointer-events: none;
opacity: 0.8;
visibility: hidden;
-moz-box-shadow: 0 0 15px #aaa;
-webkit-box-shadow: 0 0 15px #aaa;
box-shadow: 0 0 15px #aaa;
}
.close {
float: right;
margin-top: 1 px;
}
.multiple-choice {
padding: 3px 0;
}
.radius {
fill-opacity: 0.15;
stroke: #333;
stroke-dasharray: 4 2;
z-index: 1000;
fill: #bff4ff;
display: none;
}
#panel {
position: absolute;
left: 1030px;
top: 0px;
width: 300px;
padding-top: 50px;
}
.list-item {
padding: 10px;
}
label {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
#hitnumber {
font-family: Arial, Helvetica, sans-serif;
padding-left: 10px;
}
.hide {
display: none;
}
<script src="https://cdn.jsdelivr.net/npm/d3-quadtree@3"></script>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script src='https://npmcdn.com/@turf/turf/turf.min.js'></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://unpkg.com/[email protected]/dist/geo-albers-usa-territories.js"></script>
<div id="content"></div>
<div id="panel">
<div id="list">
<div id="hitnumber"></div>
<pre><div id="hits"></div></pre>
</div>
</div>
<div id="chart-title"></div>
uj5u.com熱心網友回復:
雖然我不想為此使用強制布局,但我將使用您在此處提供的代碼(盡管此處未解決將圓圈連接到其原始位置的線的問題)并快速解決為什么圓圈不要像你期望的那樣行事。
如果節點不存在,強制布局將在節點上創建適當的屬性。對于節點的位置,這些屬性是 dx 和 dy 您的資料沒有 x 或 y 屬性,因此當您創建力時,節點會使用原點 [0,0] 附近的值進行初始化,這就是它們遷移的原因到左上角。這個問題可以通過創建 x 和 y 屬性來解決:
.each(d=>[d.x,d.y] = projection([d.longitude, d.latitude]))
在下面的代碼片段中,我在輸入圓圈后立即使用它。
其次,您希望將系結資料傳遞給力布局而不是節點(否則,由于節點本身沒有 x,y 屬性,我們將再次在左上角初始化它們)。我們也不想將選擇作為節點傳遞,而是讓我們訪問選擇的資料:
simulation.nodes(forceids.data())
其中,用幾行快速而骯臟的行來重置移動的節點(使用緯度/經度,并將 x,y 重置為緯度/經度),給我們:
顯示代碼片段
let margin = { top: 0, right: 0, bottom: 10, left: 0 },
width = 1000 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
let projection = geoAlbersUsaTerritories.geoAlbersUsaTerritories()
.scale(width)
.translate([width / 2, height / 2.2]);
const path = d3.geoPath()
.projection(projection);
var simulation = d3.forceSimulation()
.force('charge', d3.forceManyBody().strength(-160))
.stop()
let eventX,
eventY
const formatDate2 = d3.timeFormat("%m-%Y")
const svg = d3.select("#content")
.append("svg")
.attr('id', 'map')
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
const map = svg.append('g')
.attr("transform", "translate(" margin.left "," margin.top ")")
.attr('class', 'map')
const landmass = map.append('g').attr('class', 'land')
const places = map.append('g').attr('id', 'places')
svg.append('ellipse').classed("radius", true).attr('id', 'locate')
d3.json("https://unpkg.com/[email protected]/states-10m.json").then(function (usa) {
landmass.selectAll('path')
.data(topojson.feature(usa, usa.objects.nation).features)
.enter().append("path")
.attr("d", path)
.attr("class", "outline")
.attr('fill', '#ccc')
.attr("stroke", "#999")
landmass.append("path")
.datum(topojson.mesh(usa, usa.objects.states, function (a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path)
.attr('fill', 'none')
.attr('stroke', 'white')
});
const data = [
{
"id": 3448,
"name": "General Edward Lawrence Logan Intl",
"city": "Boston",
"faa": "BOS",
"latitude": 42.364347,
"longitude": -71.005181
},
{
"id": 3453,
"name": "Metropolitan Oakland Intl",
"city": "Oakland",
"faa": "OAK",
"latitude": 37.721278,
"longitude": -122.220722
},
{
"id": 3454,
"name": "Eppley Afld",
"city": "Omaha",
"faa": "OMA",
"latitude": 41.303167,
"longitude": -95.894069
},
{
"id": 3457,
"name": "Wichita Mid Continent",
"city": "Wichita",
"faa": "ICT",
"latitude": 37.649944,
"longitude": -97.433056
},
{
"id": 3458,
"name": "Kansas City Intl",
"city": "Kansas City",
"faa": "MCI",
"latitude": 39.297606,
"longitude": -94.713905
},
{
"id": 3459,
"name": "Dane Co Rgnl Truax Fld",
"city": "Madison",
"faa": "MSN",
"latitude": 43.139858,
"longitude": -89.337514
},
{
"id": 3462,
"name": "Phoenix Sky Harbor Intl",
"city": "Phoenix",
"faa": "PHX",
"latitude": 33.434278,
"longitude": -112.011583
},
{
"id": 3467,
"name": "Spokane Intl",
"city": "Spokane",
"faa": "GEG",
"latitude": 47.619861,
"longitude": -117.533833
},
{
"id": 3469,
"name": "San Francisco Intl",
"city": "San Francisco",
"faa": "SFO",
"latitude": 37.618972,
"longitude": -122.374889
},
{
"id": 3472,
"name": "Gainesville Rgnl",
"city": "Gainesville",
"faa": "GNV",
"latitude": 29.690056,
"longitude": -82.271778
},
{
"id": 3473,
"name": "Memphis Intl",
"city": "Memphis",
"faa": "MEM",
"latitude": 35.042417,
"longitude": -89.976667
},
{
"id": 3484,
"name": "Los Angeles Intl",
"city": "Los Angeles",
"faa": "LAX",
"latitude": 33.942536,
"longitude": -118.408075
},
{
"id": 3486,
"name": "Cleveland Hopkins Intl",
"city": "Cleveland",
"faa": "CLE",
"latitude": 41.411689,
"longitude": -81.849794
},
{
"id": 3494,
"name": "Newark Liberty Intl",
"city": "Newark",
"faa": "EWR",
"latitude": 40.6925,
"longitude": -74.168667
},
{
"id": 3502,
"name": "Dallas Love Fld",
"city": "Dallas",
"faa": "DAL",
"latitude": 32.847111,
"longitude": -96.851778
},
{
"id": 3550,
"name": "George Bush Intercontinental",
"city": "Houston",
"faa": "IAH",
"latitude": 29.984433,
"longitude": -95.341442
},
{
"id": 3559,
"name": "El Paso Intl",
"city": "El Paso",
"faa": "ELP",
"latitude": 31.80725,
"longitude": -106.377583
},
{
"id": 3566,
"name": "William P Hobby",
"city": "Houston",
"faa": "HOU",
"latitude": 29.645419,
"longitude": -95.278889
},
{
"id": 3570,
"name": "Pittsburgh Intl",
"city": "Pittsburgh",
"faa": "PIT",
"latitude": 40.491467,
"longitude": -80.232872
},
{
"id": 3576,
"name": "Miami Intl",
"city": "Miami",
"faa": "MIA",
"latitude": 25.79325,
"longitude": -80.290556
},
{
"id": 3582,
"name": "Long Beach",
"city": "Long Beach",
"faa": "LGB",
"latitude": 33.817722,
"longitude": -118.151611
},
{
"id": 3585,
"name": "Indianapolis Intl",
"city": "Indianapolis",
"faa": "IND",
"latitude": 39.717331,
"longitude": -86.294383
},
{
"id": 3589,
"name": "Westchester Co",
"city": "White Plains",
"faa": "HPN",
"latitude": 41.066959,
"longitude": -73.707575
},
{
"id": 3697,
"name": "La Guardia",
"city": "New York",
"faa": "LGA",
"latitude": 40.777245,
"longitude": -73.872608
},
{
"id": 3747,
"name": "Chicago Midway Intl",
"city": "Chicago",
"faa": "MDW",
"latitude": 41.785972,
"longitude": -87.752417
},
{
"id": 3797,
"name": "John F Kennedy Intl",
"city": "New York",
"faa": "JFK",
"latitude": 40.639751,
"longitude": -73.778925
},
{
"id": 3830,
"name": "Chicago Ohare Intl",
"city": "Chicago",
"faa": "ORD",
"latitude": 41.978603,
"longitude": -87.904842
}
]
d3.selectAll('.close').on('click', function () {
d3.selectAll('.popup').remove()
})
data.forEach(function (d) {
d.latitude = d.latitude;
d.longitude = d.longitude;
})
d3.selectAll('.location').remove()
let locations = places.selectAll(".location")
.data(data);
locations.enter()
.append("circle")
.attr('id', d => 'n' d.id)
.attr("class", 'location')
.attr('cx', d => projection([d.longitude, d.latitude])[0])
.attr('cy', d => projection([d.longitude, d.latitude])[1])
.each(d=>[d.x,d.y] = projection([d.longitude, d.latitude]))
.attr("r", 5)
.attr('fill', 'green')
.style('stroke', '#fff')
.style('stroke-width', .5)
.style("opacity", .75)
.on('click', function (event, d) {
simulation.stop()
var isSelectedCode = d.detention_facility_code
var isSelectedName = d.name
var whichclass = d3.select(this).attr("class").split(' ');
let activeIndex = whichclass.indexOf('active')
var sel = d3.select(this);
sel.raise();
let latlng = [d.longitude, d.latitude]
$('#clickedFacility').text(d.name)
$('#slider').removeClass('hide')
showRadius(latlng, far)
})
.on("mouseover", function (event, d) {
var sel = d3.select(this);
sel.raise();
let tooltip_str = d.name
tooltip.html(tooltip_str)
.style("visibility", "visible");
})
.on("mousemove", function (event, d) {
tooltip.style("top", event.pageY - (tooltip.node().clientHeight 5) "px")
.style("left", event.pageX - (tooltip.node().clientWidth / 2.0) "px");
})
.on("mouseout", function (event, d) {
var sel = d3.select(this);
sel.lower();
tooltip.style("visibility", "hidden");
})
locations
.transition()
.duration(100)
.attr("class", d => "location " d.name.replace(/[\s]/g, '') ' ' d.type_detailed.replace(/\s |[,\/]/g, "") ' closed' d.is_closed)
.attr("r", 5)
.attr('fill', 'green')
locations.exit()
.remove();
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip");
var info = svg.append("div")
.attr("class", "info");
const quadtree = d3.quadtree()
.x(d => d.longitude)
.y(d => d.latitude)
.addAll(data);
let miles = 30
let far = (1 / 60) * miles; // in degrees
function showRadius(evt, far) {
// Reset //
svg.selectAll('circle')
.attr('cx', d => d.x = projection([d.longitude, d.latitude])[0])
.attr('cy', d => d.y = projection([d.longitude, d.latitude])[1])
.attr('fill', 'green')
/////
d3.select('ellipse.radius').remove();
let radiusCircle = map.append("ellipse").classed("radius", true).attr('id', 'locate')
let xy = projection.invert(evt)
console.log('xy', xy)
let xyObject = { "longitude": evt[0], "latitude": evt[1] }
radiusLng = evt[0] far
radiusLat = evt[1] far
radiusLngLat = [ radiusLng, radiusLat]
radiusPoint = projection(radiusLngLat)
console.log('radiusPoint', radiusPoint)
radiusX = Math.abs(evt[1] far)
radiusY = Math.abs(evt[0] far)
radiusToPoint = projection([radiusY, radiusX][0])
d3.select('ellipse.radius').classed('hide', false)
d3.select('ellipse.radius').classed('show', true)
let radiusprojx = projection(evt)[0]
let radiusprojxN = projection(evt)[0] - eventX
let radiusprojy = projection(evt)[1]
let radiusprojyN = projection(evt)[1] - eventY
radiusCircle
.attr('cx', d => (projection(evt)[0]))
.attr('cy', d => (projection(evt)[1]))
.attr('rx', 20)
.attr('ry', 20)
let hits = [];
quadtree.visit(nearest(xyObject, far, hits))
for (i = 0; i < hits.length; i ) {
let line = turf.lineString([[evt[1], evt[0]], [hits[i].latitude, hits[i].longitude]]);
let length = turf.length(line, { units: 'miles' });
hits[i].distance = length.toFixed(2) ' miles';
}
hits.sort(function (a, b) { return d3.ascending(a.distance, b.distance) })
let locationsInRadius = hits.map(a => a.id);
//console.log('locationsInRadius', locationsInRadius)
d3.selectAll('.location').attr('fill', 'green')
locationsInRadius.forEach(function (d, i) {
d3.select('#n' d).attr('fill', 'blue')
})
let total_count = hits.length
$("#hitnumber").text(total_count " airports within 30 miles")
d3.selectAll('.list-item').remove()
let listItem = d3.selectAll('#hits').selectAll('text')
.data(hits)
.attr('padding-left', '20px')
.enter().append('div').attr('class', 'list-item')
.html(d => d.name "<br/>Lat: " d.latitude "<br/>Lng: " d.longitude "<br/>Distance: " d.distance)
if (hits.length == 1) {
} else {
const hitids = []
for (i = 0; i < hits.length; i ) {
hitids.push('#n' hits[i].id)
}
let idstoget = hitids.toString()
let forceids = d3.selectAll(idstoget)
simulation.force('x', d3.forceX().strength(0.1).x(radiusPoint[0]))
simulation.force('y', d3.forceY().strength(0.1).y(radiusPoint[1]))
// simulation.force('center', d3.forceCenter(radiusPoint[0], radiusPoint[1]))
simulation.alpha(1).restart()
simulation.nodes(forceids.data())
.on('tick', ticked)
function ticked() {
update(forceids)
}
function update(forceids) {
forceids
.attr('cx', function (d) { return d.x })
.attr('cy', function (d) { return d.y })
}
}
}
function nearest(node, radius, hits) {
if (!hits) hits = [];
var r = radius,
nx1 = node.longitude - r,
nx2 = node.longitude r,
ny1 = node.latitude - r,
ny2 = node.latitude r;
return function (quad, x1, y1, x2, y2) {
if (quad.data && (quad.data !== node)) {
var x = node.longitude - quad.data.longitude,
y = node.latitude - quad.data.latitude,
l = Math.sqrt(x * x y * y),
r = radius;
if (l < r) {
hits.push(quad.data)
} else {
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
}
}
.tooltip {
position: absolute;
padding: 15px;
font: 12px sans-serif;
background: #fff;
color: #000;
border: 0px;
pointer-events: none;
opacity: 0.8;
visibility: hidden;
-moz-box-shadow: 0 0 15px #aaa;
-webkit-box-shadow: 0 0 15px #aaa;
box-shadow: 0 0 15px #aaa;
}
.close {
float: right;
margin-top: 1 px;
}
.multiple-choice {
padding: 3px 0;
}
.radius {
fill-opacity: 0.15;
stroke: #333;
stroke-dasharray: 4 2;
z-index: 1000;
fill: #bff4ff;
display: none;
}
#panel {
position: absolute;
left: 1030px;
top: 0px;
width: 300px;
padding-top: 50px;
}
.list-item {
padding: 10px;
}
label {
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
#hitnumber {
font-family: Arial, Helvetica, sans-serif;
padding-left: 10px;
}
.hide {
display: none;
}
<script src="https://cdn.jsdelivr.net/npm/d3-quadtree@3"></script>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script src='https://npmcdn.com/@turf/turf/turf.min.js'></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script src="https://unpkg.com/[email protected]/dist/geo-albers-usa-territories.js"></script>
<div id="content"></div>
<div id="panel">
<div id="list">
<div id="hitnumber"></div>
<pre><div id="hits"></div></pre>
</div>
</div>
<div id="chart-title"></div>
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/375824.html
標籤:javascript d3.js 力布局
