factor: Switch to Web Mercator coordinates
This commit is contained in:
@@ -188,6 +188,14 @@ export default {
|
||||
});
|
||||
if (renderer) renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
// Convert lat/lon to Web Mercator meters (EPSG:3857)
|
||||
const lonLatToWebMercator = (lon, lat) => {
|
||||
const x = lon * 20037508.34 / 180;
|
||||
let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
|
||||
y = y * 20037508.34 / 180;
|
||||
return { x, y };
|
||||
};
|
||||
|
||||
const initThreeJS = () => {
|
||||
scene = new THREE.Scene();
|
||||
@@ -230,10 +238,14 @@ export default {
|
||||
const ne = bounds.getNorthEast();
|
||||
const sw = bounds.getSouthWest();
|
||||
|
||||
camera.left = sw.lng;
|
||||
camera.right = ne.lng;
|
||||
camera.top = ne.lat;
|
||||
camera.bottom = sw.lat;
|
||||
// Convert lat/lon bounds to Web Mercator meters
|
||||
const neMerc = lonLatToWebMercator(ne.lng, ne.lat);
|
||||
const swMerc = lonLatToWebMercator(sw.lng, sw.lat);
|
||||
|
||||
camera.left = swMerc.x;
|
||||
camera.right = neMerc.x;
|
||||
camera.top = neMerc.y;
|
||||
camera.bottom = swMerc.y;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.render(scene, camera);
|
||||
|
||||
@@ -165,7 +165,7 @@ const settings = reactive({
|
||||
altitude: 60,
|
||||
intensity: 1.2,
|
||||
heightScale: 3,
|
||||
terrainColor: "#9a9996",
|
||||
terrainColor: 0x9A9996,
|
||||
...props.initialSettings
|
||||
});
|
||||
|
||||
@@ -249,12 +249,31 @@ const handleResize = () => {
|
||||
renderer.setSize(width, height);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
|
||||
// Always maintain square frustum regardless of canvas aspect ratio
|
||||
const viewSize = 6;
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
// Maintain tile aspect ratio in frustum
|
||||
if (geometryCache && geometryCache.tileAspect) {
|
||||
const viewSize = 6;
|
||||
const tileAspect = geometryCache.tileAspect;
|
||||
|
||||
if (tileAspect > 1) {
|
||||
camera.left = -viewSize * tileAspect;
|
||||
camera.right = viewSize * tileAspect;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
} else {
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize / tileAspect;
|
||||
camera.bottom = -viewSize / tileAspect;
|
||||
}
|
||||
} else {
|
||||
// Fallback: square frustum if no tile loaded yet
|
||||
const viewSize = 6;
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
}
|
||||
|
||||
camera.updateProjectionMatrix();
|
||||
};
|
||||
|
||||
@@ -285,17 +304,20 @@ const loadTileData = (tileData) => {
|
||||
const spanZ = tileData.bounds.maxZ - tileData.bounds.minZ;
|
||||
const maxSpan = Math.max(spanX, spanY);
|
||||
|
||||
// Normalize XY to fit in a 10-unit box
|
||||
// Calculate actual aspect ratio of the tile
|
||||
const tileAspect = spanX / spanY;
|
||||
|
||||
// Normalize XY to fit in view, maintaining actual aspect ratio
|
||||
const normalizeScale = 10 / maxSpan;
|
||||
|
||||
// CRITICAL: Use App2's adaptive Z scaling
|
||||
// This makes Z proportional to actual elevation variation
|
||||
// Z scaling: make Z variation visible but proportional
|
||||
const zScale = normalizeScale * (maxSpan * 0.1) / spanZ;
|
||||
|
||||
console.log('Tile scaling:', {
|
||||
spanX: spanX.toFixed(2),
|
||||
spanY: spanY.toFixed(2),
|
||||
spanZ: spanZ.toFixed(2),
|
||||
tileAspect: tileAspect.toFixed(3),
|
||||
normalizeScale: normalizeScale.toFixed(4),
|
||||
zScale: zScale.toFixed(4)
|
||||
});
|
||||
@@ -314,7 +336,7 @@ const loadTileData = (tileData) => {
|
||||
geometry.setIndex(new THREE.BufferAttribute(tileData.indices, 1));
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
// Cache base Z values for height exaggeration (matching App2's approach)
|
||||
// Cache base Z values for height exaggeration
|
||||
const baseZ = new Float32Array(tileData.positions.length);
|
||||
for (let i = 0; i < tileData.positions.length; i += 3) {
|
||||
baseZ[i] = 0;
|
||||
@@ -326,7 +348,8 @@ const loadTileData = (tileData) => {
|
||||
geometry,
|
||||
baseZ,
|
||||
spanZ,
|
||||
zScale
|
||||
zScale,
|
||||
tileAspect
|
||||
};
|
||||
|
||||
// Create material and mesh
|
||||
@@ -338,15 +361,25 @@ const loadTileData = (tileData) => {
|
||||
mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
|
||||
// Configure camera frustum for orthographic view - ALWAYS SQUARE
|
||||
const viewSize = 6; // 10-unit terrain + padding
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
// Configure camera frustum to match tile aspect ratio
|
||||
const viewSize = 6; // Base size for the view
|
||||
|
||||
// Adjust frustum based on tile aspect ratio
|
||||
if (tileAspect > 1) {
|
||||
// Wider than tall
|
||||
camera.left = -viewSize * tileAspect;
|
||||
camera.right = viewSize * tileAspect;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
} else {
|
||||
// Taller than wide
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize / tileAspect;
|
||||
camera.bottom = -viewSize / tileAspect;
|
||||
}
|
||||
|
||||
// Adjust near/far to accommodate height exaggeration
|
||||
// Terrain is centered at Z=0, extends ±(spanZ * zScale / 2) in base form
|
||||
const maxZExtent = (spanZ * zScale / 2) * 20; // Max exaggeration
|
||||
camera.near = 0.1;
|
||||
camera.far = 100 + maxZExtent * 2;
|
||||
@@ -523,11 +556,11 @@ const renderTileWithSettings = async (tileData, renderSettings, resolution = 102
|
||||
const originalHeight = renderer.domElement.height;
|
||||
const originalPixelRatio = renderer.getPixelRatio();
|
||||
|
||||
// Set render size (square)
|
||||
// Set render size (square for export)
|
||||
renderer.setSize(resolution, resolution);
|
||||
renderer.setPixelRatio(1);
|
||||
|
||||
// Update camera for square aspect (always square)
|
||||
// Update camera for square render output
|
||||
const viewSize = 6;
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
@@ -545,11 +578,28 @@ const renderTileWithSettings = async (tileData, renderSettings, resolution = 102
|
||||
renderer.setSize(originalWidth / originalPixelRatio, originalHeight / originalPixelRatio);
|
||||
renderer.setPixelRatio(originalPixelRatio);
|
||||
|
||||
// Restore camera (always square frustum)
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
// Restore camera with tile aspect ratio
|
||||
if (geometryCache && geometryCache.tileAspect) {
|
||||
const tileAspect = geometryCache.tileAspect;
|
||||
|
||||
if (tileAspect > 1) {
|
||||
camera.left = -viewSize * tileAspect;
|
||||
camera.right = viewSize * tileAspect;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
} else {
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize / tileAspect;
|
||||
camera.bottom = -viewSize / tileAspect;
|
||||
}
|
||||
} else {
|
||||
// Fallback: square frustum
|
||||
camera.left = -viewSize;
|
||||
camera.right = viewSize;
|
||||
camera.top = viewSize;
|
||||
camera.bottom = -viewSize;
|
||||
}
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
const endTime = performance.now();
|
||||
|
||||
Reference in New Issue
Block a user