UNPKG

3d-tiles-renderer

Version:

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification

146 lines (92 loc) 3.35 kB
import { Vector3, MathUtils } from 'three'; import { OccupancyAnnotation } from '../ScreenOccupationManager.js'; const _delta = /* @__PURE__ */ new Vector3(); // suppress annotations within ~6 degrees of the globe horizon const PERSPECTIVE_CULL_ANGLE = Math.acos( 0.1 ); export class PointAnnotation extends OccupancyAnnotation { constructor() { super(); this.position = new Vector3(); this.lat = 0; this.lon = 0; this.radius = 28; this.screenPos = new Vector3(); this._facingAngle = 0; } updateTransform( matrix, resolution, cameraPosition ) { const { position, screenPos } = this; // project to screen space screenPos.copy( position ).applyMatrix4( matrix ); // transform to resolution coordinates screenPos.x = ( screenPos.x * 0.5 + 0.5 ) * resolution.width; screenPos.y = ( - screenPos.y * 0.5 + 0.5 ) * resolution.height; screenPos.z = ( screenPos.z < - 1 || screenPos.z > 1 ) ? 1 : 0; // facing ratio: dot( surface normal, direction to camera ) // TODO: store geodetic normal on the item at creation time and use it here instead of // normalize( position ) if ( cameraPosition !== null ) { _delta.subVectors( cameraPosition, position ); this._facingAngle = position.lengthSq() > 0 ? position.angleTo( _delta ) : 0; } else { this._facingAngle = 0; } } evaluate( handle ) { const { screenPos, radius, _facingAngle } = this; if ( ! this.ready ) { return false; } if ( screenPos.z !== 0 ) { return false; } if ( _facingAngle > PERSPECTIVE_CULL_ANGLE ) { return false; } if ( handle.test( screenPos.x, screenPos.y, radius ) ) { return false; } handle.mark( screenPos.x, screenPos.y, radius ); return true; } } export function parsePointAnnotations( vectorTile, x, y, level, tiling, filter, target = [] ) { const [ tMinX, tMinY, tMaxX, tMaxY ] = tiling.getTileBounds( x, y, level, true, false ); for ( const layerName in vectorTile.layers ) { const layer = vectorTile.layers[ layerName ]; const extent = layer.extent; for ( let i = 0; i < layer.length; i ++ ) { // process only points const feature = layer.feature( i ); if ( feature.type !== 1 ) { continue; } if ( ! filter( layerName, feature.properties, feature.type ) ) { continue; } // retrieve the geometry const geometry = feature.loadGeometry(); for ( const [ point ] of geometry ) { const u = MathUtils.lerp( tMinX, tMaxX, point.x / extent ); // tile Y=0 is geographic north; with flipY the V axis increases northward // so we invert vf when flipY is set const vf = point.y / extent; const v = tiling.flipY ? MathUtils.lerp( tMaxY, tMinY, vf ) : MathUtils.lerp( tMinY, tMaxY, vf ); const [ lon, lat ] = tiling.toCartographicPoint( u, v ); const item = new PointAnnotation(); // feature.id is the OSM element ID (node/way/relation) preserved by Planetiler // across all zoom levels — stable and unique for cross-LoD annotation replacement. // TODO: is this id always guaranteed to be unique and consistent across LoDs? item.id = `${ layerName }:${ feature.id }`; item.layer = layerName; item.properties = feature.properties; item.lat = lat; item.lon = lon; item.lodLevel = level; target.push( item ); } } } return target; }