@giro3d/giro3d
Version:
A JS/WebGL framework for 3D geospatial data visualization
117 lines (113 loc) • 3.4 kB
JavaScript
/*
* Copyright (c) 2015-2018, IGN France.
* Copyright (c) 2018-2026, Giro3D team.
* SPDX-License-Identifier: MIT
*/
import { Color, FloatType, Vector2, Vector3 } from 'three';
import RenderingState from '../../renderer/RenderingState';
import Coordinates from '../geographic/Coordinates';
import CoordinateSystem from '../geographic/CoordinateSystem';
import traversePickingCircle from './PickingCircle';
/** Pick result on tiles (e.g. map) */
/**
* Tests whether an object implements {@link MapPickResult}.
*
* @param obj - Object
* @returns `true` if the object implements the interface.
*/
export const isMapPickResult = obj => obj.isMapPickResult;
const BLACK = new Color(0, 0, 0);
const tmpCoords = new Coordinates(CoordinateSystem.epsg3857, 0, 0, 0);
function renderTileBuffer(instance, map, coords, radius) {
const dim = instance.engine.getWindowSize();
coords = coords || new Vector2(Math.floor(dim.x / 2), Math.floor(dim.y / 2));
const restore = map.setRenderState(RenderingState.PICKING);
const buffer = instance.engine.renderToBuffer({
camera: instance.view.camera,
scene: map.object3d,
clearColor: BLACK,
datatype: FloatType,
zone: {
x: coords.x - radius,
y: coords.y - radius,
width: 1 + radius * 2,
height: 1 + radius * 2
}
});
restore();
const ids = [];
const uvs = [];
const zs = [];
traversePickingCircle(radius, (x, y, idx) => {
const px = idx * 4;
const id = buffer[px + 0];
const z = buffer[px + 1];
const u = buffer[px + 2];
const v = buffer[px + 3];
ids.push(id);
zs.push(z);
uvs.push(new Vector2(u, v));
return null;
});
return {
ids,
uvs,
zs
};
}
/**
* Pick tiles from a map object. This does not do any sorting
*
* @param instance - Instance to pick from
* @param canvasCoords - Coordinates on the rendering canvas
* @param map - Map object to pick from
* @param options - Options
* @returns Target
*/
function pickTilesAt(instance, canvasCoords, map, options = {}) {
const radius = options.radius ?? 0;
const limit = options.limit ?? Infinity;
const filter = options.filter;
const target = [];
const {
ids,
uvs,
zs
} = renderTileBuffer(instance, map, canvasCoords, radius);
const extent = map.extent;
const crs = extent.crs;
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const uv = uvs[i];
const z = zs[i];
const tile = map.tileIndex.getTile(id);
if (tile != null && tile.isTileMesh) {
const ex = tile.extent;
tmpCoords.set(crs, ex.minX + uv.x * (ex.maxX - ex.minX), ex.minY + uv.y * (ex.maxY - ex.minY), 0);
const elevation = z;
if (elevation != null) {
tmpCoords.values[2] = elevation;
// convert to instance crs
// here (and only here) should be the Coordinates instance creation
const coord = tmpCoords.as(instance.coordinateSystem);
const point = tmpCoords.toVector3(new Vector3());
const p = {
isMapPickResult: true,
object: tile,
entity: map,
point,
coord,
distance: instance.view.camera.position.distanceTo(point)
};
if (!filter || filter(p)) {
target.push(p);
if (target.length >= limit) {
break;
}
}
}
}
}
return target;
}
export default pickTilesAt;