是否可以从
LineString
中的 MultiLineString
创建单个连续的 geojson
,而不弄乱线条的几何形状?
(想想混乱的多线道路重叠和小间隙也会翻转方向)
这段 Node JS 代码表现不佳:
const fs = require('fs');
// Load the GeoJSON data
const geojson = JSON.parse(fs.readFileSync('Route20OK.geojson', 'utf8'));
// Helper function to calculate the distance between two coordinates
function calculateDistance(coord1, coord2) {
const [lon1, lat1] = coord1;
const [lon2, lat2] = coord2;
const R = 6371000; // Earth radius in meters
const dLat = (lat2 - lat1) * (Math.PI / 180);
const dLon = (lon2 - lon1) * (Math.PI / 180);
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) *
Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
// Step 1: Find the two most "extreme" endpoints
function findExtremeEndpoints(segments) {
const endpoints = [];
// Collect all endpoints
segments.forEach(segment => {
endpoints.push(segment[0], segment[segment.length - 1]);
});
// Calculate a score for each endpoint by summing its distances to all other endpoints
const scores = endpoints.map((endpoint, i) => {
let score = 0;
endpoints.forEach((other, j) => {
if (i !== j) score += calculateDistance(endpoint, other);
});
return { point: endpoint, score };
});
// Sort by score and select the top two endpoints as the extreme endpoints
scores.sort((a, b) => b.score - a.score);
return [scores[0].point, scores[1].point];
}
// Step 2: Connect segments into a continuous path with orientation checks
function connectSegments(segments, extremeEndpoints) {
const [extremeStart, extremeEnd] = extremeEndpoints;
let path = [extremeStart];
let currentPoint = extremeStart;
const gapTolerance = 50; // meters
while (segments.length > 0) {
let closestIndex = -1;
let closestSegment = null;
let closestDistance = Infinity;
let reverse = false;
// Find the closest segment to connect to the current path end
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
const start = segment[0];
const end = segment[segment.length - 1];
const distanceToStart = calculateDistance(currentPoint, start);
const distanceToEnd = calculateDistance(currentPoint, end);
if (distanceToStart < closestDistance) {
closestDistance = distanceToStart;
closestIndex = i;
closestSegment = segment;
reverse = false;
}
if (distanceToEnd < closestDistance) {
closestDistance = distanceToEnd;
closestIndex = i;
closestSegment = segment;
reverse = true;
}
}
if (closestSegment) {
// Reverse the segment if necessary
if (reverse) closestSegment.reverse();
// Add segment to the path, projecting midpoint for small gaps
if (closestDistance <= gapTolerance) {
const midPoint = [
(currentPoint[0] + closestSegment[0][0]) / 2,
(currentPoint[1] + closestSegment[0][1]) / 2
];
path.push(midPoint);
}
path.push(...closestSegment);
currentPoint = path[path.length - 1];
segments.splice(closestIndex, 1); // Remove the segment from the list
} else {
break; // Exit if no more segments can connect
}
}
return path;
}
// Process each feature in the GeoJSON
geojson.features = geojson.features.map(feature => {
if (feature.geometry.type === 'MultiLineString') {
const segments = feature.geometry.coordinates;
// Step 1: Identify major endpoints using distance scoring
const extremeEndpoints = findExtremeEndpoints(segments);
// Step 2: Connect segments to form a continuous path
const orderedCoordinates = connectSegments(segments, extremeEndpoints);
// Update the geometry to a LineString with the ordered coordinates
feature.geometry = {
type: 'LineString',
coordinates: orderedCoordinates
};
}
return feature;
});
// Write the modified GeoJSON to a new file
fs.writeFileSync('Route20Final.geojson', JSON.stringify(geojson, null, 2));
console.log('GeoJSON has been processed and saved to output.geojson');
链接到下载 geojson
我认为你需要将代码从单个巨型几何体分解为多个几何体。
From
To
demo.js
const fs = require('fs');
const geojson = JSON.parse(fs.readFileSync('Route20OK.geojson', 'utf8'));
// Function to split each segment into separate LineString features
function splitMultiLineString(feature) {
const segments = feature.geometry.coordinates;
return segments.map(segment => ({
type: "Feature",
properties: { ...feature.properties },
geometry: {
type: "LineString",
coordinates: segment
}
}));
}
// Process each feature in the GeoJSON
let newFeatures = [];
geojson.features.forEach(feature => {
if (feature.geometry.type === 'MultiLineString') {
// Split the MultiLineString into multiple LineString features
const lineStringFeatures = splitMultiLineString(feature);
newFeatures.push(...lineStringFeatures);
} else {
newFeatures.push(feature);
}
});
geojson.features = newFeatures;
fs.writeFileSync('Route20Final.geojson', JSON.stringify(geojson, null, 2));
console.log('GeoJSON has been processed and saved to Route20Final.geojson');