UNPKG

3d-tiles-renderer

Version:

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

253 lines (148 loc) 4.41 kB
import { MathUtils } from 'three'; // Class for storing and querying a certain projection scheme for an image and converting // between the [0, 1] image range to cartographic longitude / latitude values. export class ProjectionScheme { get isMercator() { return this.scheme === 'EPSG:3857'; } get isCartographic() { return this.scheme !== 'none'; } constructor( scheme = 'EPSG:4326' ) { this.scheme = scheme; this.tileCountX = 1; this.tileCountY = 1; this.setScheme( scheme ); } setScheme( scheme ) { this.scheme = scheme; switch ( scheme ) { // equirect case 'CRS:84': case 'EPSG:4326': this.tileCountX = 2; this.tileCountY = 1; break; // mercator case 'EPSG:3857': this.tileCountX = 1; this.tileCountY = 1; break; case 'none': this.tileCountX = 1; this.tileCountY = 1; break; default: throw new Error( `ProjectionScheme: Unknown projection scheme "${ scheme }"` ); } } convertNormalizedToLatitude( v ) { if ( this.scheme === 'none' ) { return v; } else if ( this.isMercator ) { // https://gis.stackexchange.com/questions/447421/convert-a-point-on-a-flat-2d-web-mercator-map-image-to-a-coordinate const ratio = MathUtils.mapLinear( v, 0, 1, - 1, 1 ); return 2 * Math.atan( Math.exp( ratio * Math.PI ) ) - Math.PI / 2; } else { return MathUtils.mapLinear( v, 0, 1, - Math.PI / 2, Math.PI / 2 ); } } convertNormalizedToLongitude( v ) { if ( this.scheme === 'none' ) { return v; } else { return MathUtils.mapLinear( v, 0, 1, - Math.PI, Math.PI ); } } convertLatitudeToNormalized( lat ) { if ( this.scheme === 'none' ) { return lat; } else if ( this.isMercator ) { // https://stackoverflow.com/questions/14329691/convert-latitude-longitude-point-to-a-pixels-x-y-on-mercator-projection const mercatorN = Math.log( Math.tan( ( Math.PI / 4 ) + ( lat / 2 ) ) ); return ( 1 / 2 ) + ( 1 * mercatorN / ( 2 * Math.PI ) ); } else { return MathUtils.mapLinear( lat, - Math.PI / 2, Math.PI / 2, 0, 1 ); } } convertLongitudeToNormalized( lon ) { if ( this.scheme === 'none' ) { return lon; } else { return ( lon + Math.PI ) / ( 2 * Math.PI ); } } getLongitudeDerivativeAtNormalized( value ) { if ( this.scheme === 'none' ) { return 1; } else { return 2 * Math.PI; } } getLatitudeDerivativeAtNormalized( value ) { if ( this.scheme === 'none' ) { return 1; } else { const EPS = 1e-5; let yp = value - EPS; if ( yp < 0 ) { yp = value + EPS; } if ( this.isMercator ) { // TODO: why is this 2 * Math.PI rather than Math.PI? return Math.abs( this.convertNormalizedToLatitude( value ) - this.convertNormalizedToLatitude( yp ) ) / EPS; } else { return Math.PI; } } } getBounds() { if ( this.scheme === 'none' ) { return [ 0, 0, 1, 1 ]; } else { return [ this.convertNormalizedToLongitude( 0 ), this.convertNormalizedToLatitude( 0 ), this.convertNormalizedToLongitude( 1 ), this.convertNormalizedToLatitude( 1 ), ]; } } toNormalizedPoint( x, y ) { const result = [ x, y ]; result[ 0 ] = this.convertLongitudeToNormalized( result[ 0 ] ); result[ 1 ] = this.convertLatitudeToNormalized( result[ 1 ] ); return result; } toNormalizedRange( range ) { return [ ...this.toNormalizedPoint( range[ 0 ], range[ 1 ] ), ...this.toNormalizedPoint( range[ 2 ], range[ 3 ] ), ]; } toCartographicPoint( x, y ) { const result = [ x, y ]; result[ 0 ] = this.convertNormalizedToLongitude( result[ 0 ] ); result[ 1 ] = this.convertNormalizedToLatitude( result[ 1 ] ); return result; } toCartographicRange( range ) { return [ ...this.toCartographicPoint( range[ 0 ], range[ 1 ] ), ...this.toCartographicPoint( range[ 2 ], range[ 3 ] ), ]; } clampToBounds( range, normalized = false ) { const result = [ ...range ]; let clampBounds; if ( normalized ) { clampBounds = [ 0, 0, 1, 1 ]; } else { clampBounds = this.getBounds(); } const [ minX, minY, maxX, maxY ] = clampBounds; result[ 0 ] = MathUtils.clamp( result[ 0 ], minX, maxX ); result[ 2 ] = MathUtils.clamp( result[ 2 ], minX, maxX ); result[ 1 ] = MathUtils.clamp( result[ 1 ], minY, maxY ); result[ 3 ] = MathUtils.clamp( result[ 3 ], minY, maxY ); return result; } }