import Loader from "./Loader" import GeoMath from "./GeoMath"; import GeoPoint from "./GeoPoint"; import CredentialMode from "./CredentialMode"; import MarkerLineEntity from "./MarkerLineEntity"; import PolygonEntity from "./PolygonEntity"; import PinEntity from "./PinEntity"; import Resource, { URLResource, ResourceType } from "./Resource"; import AltitudeMode from "./AltitudeMode"; /** * GeoJSON形式(<a href="https://tools.ietf.org/html/rfc7946">rfc7946</a>)のデータをシーンに読み込みます。 * @memberof mapray */ class GeoJSONLoader extends Loader { /** * @desc * <p>url で指定したシーンデータの読み込みを開始し、scene にエンティティを構築する。</p> * <p>読み込みが終了したとき options.callback を呼び出す。</p> * @param {mapray.Scene} scene 読み込み先のシーン * @param {string} resource シーンファイルの URL * @param {object} [options] オプション集合 * @param {mapray.Loader.TransformCallback} [options.transform] リソース要求変換関数 * @param {mapray.GeoJSONLoader.FinishCallback} [options.callback] 終了コールバック関数 * @param {mapray.Loader.EntityCallback} [options.onEntity] エンティティコールバック関数 */ constructor( scene, resource, options={} ) { if (resource instanceof Resource) { // OK } else if ( typeof resource === "string" ) { resource = new URLResource(resource, { type: "json", transform: options.transform }); } else { throw new Error( "Unsupported Resource: " + resource); } super( scene, resource, { onEntity: options.onEntity, onLoad: options.onLoad } ); // PinEntity this._getPointFGColor = options.getPointFGColor || defaultGetPointFGColorCallback; this._getPointBGColor = options.getPointBGColor || defaultGetPointBGColorCallback; this._getPointSize = options.getPointSize || defaultGetPointSizeCallback; this._getPointIconId = options.getPointIconId || defaultGetPointIconIdCallback; // MarkerLineEntity this._getLineColor = options.getLineColor || defaultGetLineColorCallback; this._getLineWidth = options.getLineWidth || defaultGetLineWidthCallback; // PolygonEntity this._getFillColor = options.getFillColor || defaultGetFillColorCallback; this._getExtrudedHeight = options.getExtrudedHeight || defaultGetExtrudedHeightCallback; // Common this._getAltitudeMode = options.getAltitudeMode || defaultGetAltitudeModeCallback; this._getAltitude = options.getAltitude || defaultGetAltitudeCallback; this._glenv = scene.glenv; this._references = {}; this._cancelled = false; this._finished = false; } /** * @summary 読み込み処理の実態。継承クラスによって実装される。 * @private */ _load() { return ( this._resource.load( { type: ResourceType.JSON } ) .then( geoJson => { // JSON データの取得に成功 this._check_cancel(); this._load_geojson_object( geoJson ); } ) ); } /** * Load GeoJSON Object * @private */ _load_geojson_object( geojson ) { var success; if ( geojson.type === TYPES.FEATURE_COLLECTION ) { var features = geojson.features; success = false; for ( var i = 0, len = features.length; i < len; i++ ) { var feature = features[i]; var s = this._load_geojson_object( feature.featureId ? feature.feature : feature ); // @ToDo: Unknown // var s = this._load_geojson_object( feature ); if (s && !success) success = s; } } else if ( geojson.type === TYPES.FEATURE ) { var geometry = geojson.geometry; success = this._load_geometry_object( geometry, geojson ); } else if ( SUPPORTED_GEOMETRY_TYPES.indexOf( geojson.type ) !== -1 ) { success = this._load_geometry_object( geojson, null ); } else { throw new Error( "Unnsupported Type: " + geojson.type ); } if ( this._cancelled ) return false; return success; } /** * Load Geometry Object * @private */ _load_geometry_object( geometry, geojson={} ) { var coords = geometry.coordinates; if (!coords && !geometry) { return false; } switch ( geometry.type ) { case GEOMETRY_TYPES.POINT: case GEOMETRY_TYPES.MULTI_POINT: return this._loadPoint( geometry, geojson ); case GEOMETRY_TYPES.LINE_STRING: case GEOMETRY_TYPES.MULTI_LINE_STRING: return this._loadLines( geometry, geojson ); case GEOMETRY_TYPES.POLYGON: case GEOMETRY_TYPES.MULTI_POLYGON: return this._loadPolygons( geometry, geojson ); case GEOMETRY_TYPES.GEOMETRY_COLLECTION: return true; default: throw new Error( "Invalid GeoJSON type: " + geometry.type ); } } /** * fetch() の init 引数に与えるオブジェクトを生成 * @private */ _make_fetch_params( tr ) { var init = { signal: this._abort_ctrl.signal, credentials: (tr.credentials || CredentialMode.OMIT).credentials }; if ( tr.headers ) { init.headers = (tr.headers || GeoJSONLoader._defaultHeaders); } return init; } /** * @private */ _loadLines( geometry, geojson ) { const color4 = this._getLineColor( geojson ); const width = this._getLineWidth( geojson ); const altitude = this._getAltitude( geojson ); const altitude_mode = this._getAltitudeMode( geojson ); if ( !geometry || color4.length !== 4 ) { return false; } var type = geometry.type; var coords = geometry.coordinates; var rgb = color4.slice(0, 3); var alpha = color4[3]; // If multiline, split entity if ( type === GEOMETRY_TYPES.MULTI_LINE_STRING ) { coords.forEach( points => { if ( !this._generateLine( points, width, rgb, alpha, altitude_mode, altitude, geojson ) ) { return false; } }); return true; } else { // type === GEOMETRY_TYPES.LINE_STRING return this._generateLine( coords, width, rgb, alpha, altitude_mode, altitude, geojson ) } } /** * @private */ _generateLine( points, width, color, opaticy, altitude_mode, altitude, geojson ) { if ( !points ) { return false; } var entity = new MarkerLineEntity( this._scene ); entity.altitude_mode = altitude_mode; var fp = this._flatten( points, altitude ); entity.addPoints( fp ); entity.setLineWidth( width ); entity.setColor( color ); entity.setOpacity( opaticy ); this._onEntity( this, entity, geojson ); return true; } /** * @private */ _loadPoint( geometry, geojson ) { const fgColor = this._getPointFGColor( geojson ); const bgColor = this._getPointBGColor( geojson ); const iconId = this._getPointIconId( geojson ); const size = this._getPointSize( geojson ); const altitude_mode = this._getAltitudeMode( geojson ); const altitude = this._getAltitude( geojson ); if ( !geometry ) { return false; } var type = geometry.type; var props = { "fg_color": fgColor.slice( 0, 3 ), "bg_color": bgColor.slice( 0, 3 ), size: size, }; // If multiline, split entity if ( type === GEOMETRY_TYPES.POINT ) { var entity = new PinEntity( this._scene ); entity.altitude_mode = altitude_mode; var alt = this._getActualValue( altitude, geometry.coordinates[2], GeoJSONLoader.defaultAltitude ); var coords = new GeoPoint( geometry.coordinates[0], geometry.coordinates[1], alt ); if ( iconId !== null ) { entity.addMakiIconPin( iconId, coords, props ); } else { entity.addPin( coords, props ); } this._onEntity( this, entity, geojson ); } else { // type === GEOMETRY_TYPES.MULTI_POINT var entity = new PinEntity( this._scene ); entity.altitude_mode = altitude_mode; for ( var i = 0; i < geometry.coordinates.length; i++ ) { var targetCoordinates = geometry.coordinates[i]; var alt = this._getActualValue( altitude, geometry.coordinates[2], GeoJSONLoader.defaultAltitude ); var coords = new GeoPoint( targetCoordinates[0], targetCoordinates[1], alt ); if ( iconId !== null ) { entity.addMakiIconPin( iconId, coords, props ); // entity.addPin( coords, props ); } else { entity.addPin( coords, props ); } } this._onEntity( this, entity, geojson ); } return true; } /** * @private */ _loadPolygons( geometry, geojson ) { const color4 = this._getFillColor( geojson ); const altitude_mode = this._getAltitudeMode( geojson ); const altitude = this._getAltitude( geojson ); const extruded_height = this._getExtrudedHeight( geojson ); if ( !geometry || color4.length !== 4 ) { return false; } var type = geometry.type; var coords = geometry.coordinates; var rgb = color4.slice(0, 3); var alpha = color4[3]; // If multiline, split entity if ( type === GEOMETRY_TYPES.MULTI_POLYGON ) { coords.forEach( points => { if ( !this._generatePolygon( points, rgb, alpha, altitude_mode, altitude, extruded_height, geojson ) ) { return false; } }); return true; } else { // type === GEOMETRY_TYPES.POLYGON return this._generatePolygon( coords, rgb, alpha, altitude_mode, altitude, extruded_height, geojson ) } } /** * @private */ _generatePolygon( pointsList, color, opaticy, altitude_mode, altitude, extruded_height, geojson ) { if ( !pointsList ) { return false; } const entity = new PolygonEntity( this._scene ); entity.altitude_mode = altitude_mode; entity.extruded_height = extruded_height; entity.setColor( color ); entity.setOpacity( opaticy ); for ( let i=0; i< pointsList.length; i++ ) { const fp = this._flatten( pointsList[ i ], altitude, pointsList[ i ].length-1 ); if ( !fp ) return false; if ( i === 0 ) entity.addOuterBoundary( fp ); else entity.addInnerBoundary( fp ); } this._onEntity( this, entity, geojson ); return true; } /** * @private */ _getActualValue( valueFromCallback, valueInGeoJSON, defaultValue ) { return ( valueFromCallback != null ? valueFromCallback: // value from callback is the most prioritized valueInGeoJSON != null ? valueInGeoJSON: // value in GeoJSON will be used if defined defaultValue // default value ); } /** * @private */ _flatten( ary, altitude, len=ary.length ) { return ary.reduce( (p, c, i) => ( i >= len ? p : p.concat( c.slice(0, 2), this._getActualValue( altitude, c[2], GeoJSONLoader.defaultAltitude ) ) ), [] ); }; } { GeoJSONLoader._defaultHeaders = {}; GeoJSONLoader.defaultLineColor = [0, 0, 0, 1]; GeoJSONLoader.defaultFillColor = [0, 0, 0, 1]; GeoJSONLoader.defaultLineWidth = 1; GeoJSONLoader.defaultPointFGColor = [1.0, 1.0, 1.0]; GeoJSONLoader.defaultPointBGColor = [0.35, 0.61, 0.81]; GeoJSONLoader.defaultPointSize = 30; GeoJSONLoader.defaultPointIconId = null; GeoJSONLoader.defaultAltitude = 0.0; GeoJSONLoader.defaultExtrudedHeight = 0.0; } function defaultGetLineColorCallback( geojson ) { return GeoJSONLoader.defaultLineColor; } function defaultGetLineWidthCallback( geojson ) { return GeoJSONLoader.defaultLineWidth; } function defaultGetFillColorCallback( geojson ) { return GeoJSONLoader.defaultFillColor; } function defaultGetPointFGColorCallback( geojson ) { return GeoJSONLoader.defaultPointFGColor; } function defaultGetPointBGColorCallback( geojson ) { return GeoJSONLoader.defaultPointBGColor; } function defaultGetPointSizeCallback( geojson ) { return GeoJSONLoader.defaultPointSize; } function defaultGetPointIconIdCallback( geojson ) { return GeoJSONLoader.defaultPointIconId; } function defaultGetAltitudeModeCallback( geojson ) { return AltitudeMode.ABSOLUTE; } function defaultGetAltitudeCallback( geojson ) { return null; } function defaultGetExtrudedHeightCallback( geojson ) { return GeoJSONLoader.defaultExtrudedHeight; } var TYPES = { FEATURE: "Feature", FEATURE_COLLECTION: "FeatureCollection", }; var GEOMETRY_TYPES = { POINT: "Point", MULTI_POINT: "MultiPoint", LINE_STRING: "LineString", MULTI_LINE_STRING: "MultiLineString", POLYGON: "Polygon", MULTI_POLYGON: "MultiPolygon", GEOMETRY_COLLECTION: "GeometryCollection", FEATURE: "Feature" }; var SUPPORTED_GEOMETRY_TYPES = Object.values( GEOMETRY_TYPES ); export default GeoJSONLoader;