我正在尝试使用 SVG 来表示机场及其空域。我已经三次检查了坐标,但 y 轴及其转换方式看起来仍然有问题。当您加载下面的代码时,代表跑道 09L / 27R 的行应高于 09R / 27L,并且 LON 应高于两条线。使用 svg 是否有一些魔术可以做到这一点,或者我的逻辑是错误的?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Radar Screen</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden; /* Prevent scrolling */
}
#radar-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
svg {
width: 100%; /* Take full width of container */
height: 100%; /* Take full height of container */
border: 1px solid black;
}
</style>
</head>
<body>
<div id="radar-container">
<svg id="radar-svg">
<!-- Runways, waypoints, and airport will be dynamically drawn here -->
</svg>
</div>
<script>
// Function to convert latitude and longitude to screen coordinates
function convertCoordinates(latitude, longitude) {
// Airport coordinates
const airportLatitude = 51.4775; // Decimal degrees
const airportLongitude = -0.46139; // Decimal degrees
// Calculate center of SVG based on airport coordinates
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
// Assume some mapping between latitude and y-axis, and longitude and x-axis
// You may need to adjust these calculations based on your specific mapping
const scaleFactor = 2000; // Adjust based on the scale of your radar screen
const offsetX = centerX - (airportLongitude * scaleFactor);
const offsetY = centerY - (airportLatitude * scaleFactor);
const screenX = (longitude * scaleFactor) + offsetX;
const screenY = (latitude * scaleFactor) + offsetY;
return { x: screenX, y: screenY };
}
// Waypoint coordinates
const waypointCoordinates = [
{ name: "LON", latitude: 51.4861, longitude: -0.4666 },
];
// Runway coordinates
const runwayCoordinates = [
{ id: "27R", start: { latitude: 51.4776, longitude: -0.4332 }, end: { latitude: 51.4775, longitude: -0.4849 }},
{ id: "09L", start: { latitude: 51.4775, longitude: -0.4849 }, end: { latitude: 51.4776, longitude: -0.4332 }},
{ id: "09R", start: { latitude: 51.4647, longitude: -0.4823 }, end: { latitude: 51.4655, longitude: -0.4341 }},
{ id: "27L", start: { latitude: 51.4655, longitude: -0.4341 }, end: { latitude: 51.4647, longitude: -0.4823 }}
];
// Get the SVG container
const svgContainer = document.getElementById('radar-svg');
// Function to draw waypoints
function drawWaypoints() {
// Draw new waypoints
waypointCoordinates.forEach(waypoint => {
const position = convertCoordinates(waypoint.latitude, waypoint.longitude);
console.log(`Waypoint ${waypoint.name} position: (${position.x}, ${position.y})`);
// Draw square representing waypoint
const square = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
square.setAttribute('x', position.x - 5); // Adjust position to center square
square.setAttribute('y', position.y - 5); // Adjust position to center square
square.setAttribute('width', 10);
square.setAttribute('height', 10);
square.setAttribute('fill', 'blue');
svgContainer.appendChild(square);
// Add label for the waypoint
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', position.x + 10); // Offset label position
label.setAttribute('y', position.y - 10); // Offset label position
label.setAttribute('text-anchor', 'start'); // Align text to start of label
label.setAttribute('dominant-baseline', 'middle'); // Center the text vertically
label.setAttribute('font-family', 'Arial');
label.setAttribute('font-size', '12');
label.setAttribute('fill', 'black');
label.textContent = waypoint.name;
svgContainer.appendChild(label);
});
}
// Function to draw runways
function drawRunways() {
// Draw new runways
runwayCoordinates.forEach(runway => {
const start = convertCoordinates(runway.start.latitude, runway.start.longitude);
const end = convertCoordinates(runway.end.latitude, runway.end.longitude);
console.log(`Runway ${runway.id} start: (${start.x}, ${start.y})`);
console.log(`Runway ${runway.id} end: (${end.x}, ${end.y})`);
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', start.x);
line.setAttribute('y1', start.y);
line.setAttribute('x2', end.x);
line.setAttribute('y2', end.y);
line.setAttribute('stroke', 'black');
line.setAttribute('stroke-width', '2');
svgContainer.appendChild(line);
// Add label
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', start.x + 10); // Offset label position
label.setAttribute('y', start.y - 10); // Offset label position
label.setAttribute('text-anchor', 'start'); // Align text to start of label
label.setAttribute('dominant-baseline', 'middle'); // Center the text vertically
label.setAttribute('font-family', 'Arial');
label.setAttribute('font-size', '12');
label.setAttribute('fill', 'black');
label.textContent = runway.id;
svgContainer.appendChild(label);
});
}
// Draw waypoints and runways initially
drawWaypoints();
drawRunways();
</script>
</body>
</html>
我尝试通过以下方式反转 Y 轴:
const screenY = -(latitude * scaleFactor) + offsetY;
我也检查了三倍坐标。
我向您的
waypointCoordinates
对象添加了四个新点,即:北、东、南和西。
通过此更改,您可以清楚地看到 NORTH 位置低于 LON,SOUTH 高于 LON。
一种解决方案确实是将返回的对象从
convertCoordinates()
更改为 return { x: screenX, y: window.innerHeight - screenY };
正如 Jaromanda 在您问题的评论中提到的。
这里的问题是,对于经度和纬度,纬度原点位于赤道(即
0
),并且随着我们向北移动而增加。然而,SVG 的原点是左上角,并随着向下移动而增加。为此,我们需要对上面返回的对象中的screenY
加上负号,并通过添加window.innerHeight
来调整垂直屏幕位置。
为了更容易看到,我在左上角添加了一个翻转复选框,该复选框位于容器上方,这要归功于将
z-index: -1
添加到 radar-container
div
。
const flipCheckbox = document.getElementById("flipCheckbox");
flipCheckbox.addEventListener("click", () => {
while (svgContainer.lastChild) {
svgContainer.removeChild(svgContainer.lastChild);
}
drawWaypoints();
drawRunways();
});
// Function to convert latitude and longitude to screen coordinates
function convertCoordinates(latitude, longitude) {
// Airport coordinates
const airportLatitude = 51.4775; // Decimal degrees
const airportLongitude = -0.46139; // Decimal degrees
// Calculate center of SVG based on airport coordinates
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
// Assume some mapping between latitude and y-axis, and longitude and x-axis
// You may need to adjust these calculations based on your specific mapping
const scaleFactor = 2000; // Adjust based on the scale of your radar screen
const offsetX = centerX - (airportLongitude * scaleFactor);
const offsetY = centerY - (airportLatitude * scaleFactor);
const screenX = (longitude * scaleFactor) + offsetX;
const screenY = (latitude * scaleFactor) + offsetY;
return flipCheckbox.checked ? {
x: screenX,
y: window.innerHeight - screenY
} : {
x: screenX,
y: screenY
};
}
// Waypoint coordinates
const waypointCoordinates = [{
name: "LON",
latitude: 51.4861,
longitude: -0.4666
},
{
name: "EAST",
latitude: 51.4861,
longitude: -0.3375
},
{
name: "NORTH",
latitude: 51.5133,
longitude: -0.4666
},
{
name: "SOUTH",
latitude: 51.4123,
longitude: -0.4666
},
{
name: "WEST",
latitude: 51.4861,
longitude: -0.5479
}
];
// Runway coordinates
const runwayCoordinates = [{
id: "27R",
start: {
latitude: 51.4776,
longitude: -0.4332
},
end: {
latitude: 51.4775,
longitude: -0.4849
}
},
{
id: "09L",
start: {
latitude: 51.4775,
longitude: -0.4849
},
end: {
latitude: 51.4776,
longitude: -0.4332
}
},
{
id: "09R",
start: {
latitude: 51.4647,
longitude: -0.4823
},
end: {
latitude: 51.4655,
longitude: -0.4341
}
},
{
id: "27L",
start: {
latitude: 51.4655,
longitude: -0.4341
},
end: {
latitude: 51.4647,
longitude: -0.4823
}
}
];
// Get the SVG container
const svgContainer = document.getElementById('radar-svg');
// Function to draw waypoints
function drawWaypoints() {
// Draw new waypoints
waypointCoordinates.forEach(waypoint => {
const position = convertCoordinates(waypoint.latitude, waypoint.longitude);
console.log(`Waypoint ${waypoint.name} position: (${position.x}, ${position.y})`);
// Draw square representing waypoint
const square = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
square.setAttribute('x', position.x - 5); // Adjust position to center square
square.setAttribute('y', position.y - 5); // Adjust position to center square
square.setAttribute('width', 10);
square.setAttribute('height', 10);
square.setAttribute('fill', 'blue');
svgContainer.appendChild(square);
// Add label for the waypoint
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', position.x + 10); // Offset label position
label.setAttribute('y', position.y - 10); // Offset label position
label.setAttribute('text-anchor', 'start'); // Align text to start of label
label.setAttribute('dominant-baseline', 'middle'); // Center the text vertically
label.setAttribute('font-family', 'Arial');
label.setAttribute('font-size', '12');
label.setAttribute('fill', 'black');
label.textContent = waypoint.name;
svgContainer.appendChild(label);
});
}
// Function to draw runways
function drawRunways() {
// Draw new runways
runwayCoordinates.forEach(runway => {
const start = convertCoordinates(runway.start.latitude, runway.start.longitude);
const end = convertCoordinates(runway.end.latitude, runway.end.longitude);
console.log(`Runway ${runway.id} start: (${start.x}, ${start.y})`);
console.log(`Runway ${runway.id} end: (${end.x}, ${end.y})`);
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', start.x);
line.setAttribute('y1', start.y);
line.setAttribute('x2', end.x);
line.setAttribute('y2', end.y);
line.setAttribute('stroke', 'black');
line.setAttribute('stroke-width', '2');
svgContainer.appendChild(line);
// Add label
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', start.x + 10); // Offset label position
label.setAttribute('y', start.y - 10); // Offset label position
label.setAttribute('text-anchor', 'start'); // Align text to start of label
label.setAttribute('dominant-baseline', 'middle'); // Center the text vertically
label.setAttribute('font-family', 'Arial');
label.setAttribute('font-size', '12');
label.setAttribute('fill', 'black');
label.textContent = runway.id;
svgContainer.appendChild(label);
});
}
// Draw waypoints and runways initially
drawWaypoints();
drawRunways();
body {
margin: 0;
padding: 0;
overflow: hidden;
/* Prevent scrolling */
}
#radar-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
svg {
width: 100%;
/* Take full width of container */
height: 100%;
/* Take full height of container */
border: 1px solid black;
}
Flip Coordinates: <input type="checkbox" id="flipCheckbox">
<div id="radar-container">
<svg id="radar-svg">
<!-- Runways, waypoints, and airport will be dynamically drawn here -->
</svg>
</div>