itowns
Version:
A JS/WebGL framework for 3D geospatial data visualization
233 lines (232 loc) • 8.56 kB
JavaScript
import { TextureLoader, DataTexture, RedFormat, FloatType } from 'three';
const TEXTURE_TILE_DIM = 256;
const TEXTURE_TILE_SIZE = TEXTURE_TILE_DIM * TEXTURE_TILE_DIM;
const textureLoader = new TextureLoader();
function checkResponse(response) {
if (!response.ok) {
const error = new Error(`Error loading ${response.url}: status ${response.status}`);
error.response = response;
throw error;
}
}
const arrayBuffer = function (url) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return fetch(url, options).then(response => {
checkResponse(response);
return response.arrayBuffer();
});
};
/**
* Utilitary to fetch resources from a server using the [fetch API](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch).
*
* @module Fetcher
*/
export default {
/**
* Wrapper over fetch to get some text.
*
* @param {string} url - The URL of the resources to fetch.
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
*
* @return {Promise<string>} Promise containing the text.
*/
text(url) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return fetch(url, options).then(response => {
checkResponse(response);
return response.text();
});
},
/**
* Little wrapper over fetch to get some JSON.
*
* @param {string} url - The URL of the resources to fetch.
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
*
* @return {Promise<Object>} Promise containing the JSON object.
*/
json(url) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return fetch(url, options).then(response => {
checkResponse(response);
return response.json();
});
},
/**
* Wrapper over fetch to get some XML.
*
* @param {string} url - The URL of the resources to fetch.
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
*
* @return {Promise<Document>} Promise containing the XML Document.
*/
xml(url) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return fetch(url, options).then(response => {
checkResponse(response);
return response.text();
}).then(text => new window.DOMParser().parseFromString(text, 'text/xml'));
},
/**
* Wrapper around [THREE.TextureLoader](https://threejs.org/docs/#api/en/loaders/TextureLoader).
*
* @param {string} url - The URL of the resources to fetch.
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
* Note that THREE.js docs mentions `withCredentials`, but it is not
* actually used in [THREE.TextureLoader](https://threejs.org/docs/#api/en/loaders/TextureLoader).
*
* @return {Promise<THREE.Texture>} Promise containing the
* [THREE.Texture](https://threejs.org/docs/api/en/textures/Texture.html).
*/
texture(url) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
let res;
let rej;
textureLoader.crossOrigin = options.crossOrigin;
const promise = new Promise((resolve, reject) => {
res = resolve;
rej = reject;
});
textureLoader.load(url, res, () => {}, event => {
const error = new Error(`Failed to load texture from URL: \`${url}\``);
error.originalEvent = event;
rej(error);
});
return promise;
},
/**
* Wrapper over fetch to get some ArrayBuffer.
*
* @param {string} url - The URL of the resources to fetch.
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
*
* @return {Promise<ArrayBuffer>} Promise containing the ArrayBuffer.
*/
arrayBuffer,
/**
* Wrapper over fetch to get some
* [THREE.DataTexture](https://threejs.org/docs/#api/en/textures/DataTexture).
*
* @param {string} url - The URL of the resources to fetch.
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
*
* @return {Promise<THREE.DataTexture>} Promise containing the DataTexture.
*/
textureFloat(url) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return arrayBuffer(url, options).then(buffer => {
if (buffer.byteLength !== TEXTURE_TILE_SIZE * Float32Array.BYTES_PER_ELEMENT) {
throw new Error(`Invalid float data from URL: \`${url}\``);
}
const data = new Float32Array(buffer);
const texture = new DataTexture(data, TEXTURE_TILE_DIM, TEXTURE_TILE_DIM, RedFormat, FloatType);
texture.internalFormat = 'R32F';
texture.needsUpdate = true;
return texture;
});
},
/**
* Wrapper over fetch to get a bunch of files sharing the same name, but
* different extensions.
*
* @param {string} baseUrl - The shared URL of the resources to fetch.
* @param {Object} extensions - An object containing arrays. The keys of
* each of this array are available fetch type, such as `text`, `json` or
* even `arrayBuffer`. The arrays contains the extensions to append after
* the `baseUrl` (see example below).
* @param {Object} options - Fetch options (passed directly to `fetch()`),
* see [the syntax for more information](
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Syntax).
*
* @return {Promise[]} An array of promises, containing all the files,
* organized by their extensions (see the example below).
*
* @example
* itowns.Fetcher.multiple('http://geo.server/shapefile', {
* // will fetch:
* // - http://geo.server/shapefile.shp
* // - http://geo.server/shapefile.dbf
* // - http://geo.server/shapefile.shx
* // - http://geo.server/shapefile.prj
* arrayBuffer: ['shp', 'dbf', 'shx'],
* text: ['prj'],
* }).then(function _(result) {
* // result looks like:
* result = {
* shp: ArrayBuffer
* dbf: ArrayBuffer
* shx: ArrayBuffer
* prj: string
* };
* });
*/
multiple(baseUrl, extensions) {
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
const promises = [];
let url;
for (const fetchType in extensions) {
if (!this[fetchType]) {
throw new Error(`${fetchType} is not a valid Fetcher method.`);
} else {
for (const extension of extensions[fetchType]) {
url = `${baseUrl}.${extension}`;
promises.push(this[fetchType](url, options).then(result => ({
type: extension,
result
})));
}
}
}
return Promise.all(promises).then(result => {
const all = {};
for (const res of result) {
all[res.type] = res.result;
}
return Promise.resolve(all);
});
},
get() {
let format = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
const [type, subtype] = format.split('/');
switch (type) {
case 'application':
switch (subtype) {
case 'geo+json':
case 'json':
return this.json;
case 'kml':
case 'gpx':
return this.xml;
case 'x-protobuf;type=mapbox-vector':
case 'gtx':
return this.arrayBuffer;
case 'isg':
case 'gdf':
default:
return this.text;
}
case 'image':
switch (subtype) {
case 'x-bil;bits=32':
return this.textureFloat;
default:
return this.texture;
}
default:
return this.texture;
}
}
};