-
onExampleChange({...props, state, setState})}
+ onExampleChange={(props) => onExampleChange({...props, state, setState})}
>
{state.error ? {state.error}
: ''}
center long/lat: {state.viewState.longitude.toFixed(3)},
- {state.viewState.latitude.toFixed(3)},
- zoom: {state.viewState.zoom.toFixed(2)}
+ {state.viewState.latitude.toFixed(3)}, zoom: {state.viewState.zoom.toFixed(2)}
- setState({...state, loadedTable: null})}
- onFileSelected={async (uploadedFile: File) => {
+ {
+ /* TODO -restore drag and drop
+ setState(state => ({...state, loadedTable: null}))}
+ onFileSelected={async (uploadedFile: File) => {
// TODO - error handling
const data = (await load(uploadedFile, LOADERS, LOADER_OPTIONS)) as Table;
- setState({
+ setState(state => ({
...state,
selectedExample: uploadedFile.name,
loadedTable: data
- });
- }}
+ }));
+ }}
/>
+ */}
setState({...state, viewState})}
+ onViewStateChange={({viewState}) => setState(state => ({...state, viewState}))}
controller={{type: MapController, maxPitch: 85}}
- getTooltip={({object}) =>
- object && {
- html: `\
-${object.properties?.name}
-${object.geometry?.coordinates?.[0]}
-${object.geometry?.coordinates?.[1]}
`,
- style: {
- backgroundColor: '#ddd',
- fontSize: '0.8em'
+ getTooltip={({object}) => {
+ const {name, ...properties} = object?.properties || {};
+ const props = Object.entries(properties)
+ .map(([key, value]) => `${key}: ${value}
`)
+ .join('\n');
+ return (
+ object && {
+ html: `\
+${name}
+${props}
+Coords: ${object.geometry?.coordinates?.[0]};${object.geometry?.coordinates?.[1]}
`,
+ style: {
+ backgroundColor: '#ddd',
+ fontSize: '0.8em'
+ }
}
- }
- }
+ );
+ }}
>
@@ -181,22 +181,19 @@ async function onExampleChange(args: {
const {selectedLoader, selectedExample, example, state, setState} = args;
const url = example.data;
- console.log('Loading', url);
try {
const data = (await load(url, LOADERS, LOADER_OPTIONS)) as Table;
- console.log('Loaded data', data);
+ console.log('Loaded data',url, data);
const viewState = {...state.viewState, ...example.viewState};
- setState({...state, selectedLoader, selectedExample, viewState, loadedTable: data});
+ setState(state => ({...state, selectedLoader, selectedExample, viewState, loadedTable: data}));
} catch (error) {
- console.log('Failed to load data', url, error);
- setState({...state, error: `Could not load ${selectedExample}: ${error.message}`});
+ console.error('Failed to load data', url, error);
+ setState(state => ({...state, error: `Could not load ${selectedExample}: ${error.message}`}));
}
}
function renderLayer({selectedExample, selectedLoader, loadedTable}) {
-
const geojson = loadedTable as GeoJSON;
- console.warn('Rendering layer with', geojson);
return [
new GeoJsonLayer({
id: `geojson-${selectedExample}(${selectedLoader})`,
diff --git a/examples/website/geospatial/components/control-panel.tsx b/examples/website/geospatial/components/control-panel.tsx
index 98d8898f40..311ddc4979 100644
--- a/examples/website/geospatial/components/control-panel.tsx
+++ b/examples/website/geospatial/components/control-panel.tsx
@@ -1,7 +1,7 @@
import styled from 'styled-components';
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {Example, INITIAL_EXAMPLE_NAME, INITIAL_LOADER_NAME} from '../examples';
+import React, {useState, useEffect} from 'react';
+import MonacoEditor from '@monaco-editor/react';
+import type {Example} from '../examples';
const Container = styled.div`
display: flex;
@@ -19,6 +19,19 @@ const Container = styled.div`
line-height: 2;
outline: none;
z-index: 100;
+ height: calc(100vh - 105px);
+ width: 500px;
+
+ .loading-indicator {
+ margin: 0;
+ text-align: center;
+ transition: opacity 300ms ease-out;
+ }
+
+ > .monaco-editor {
+ height: calc(100vh - 200px) !important;
+ width: 700px !important;
+ }
`;
const DropDown = styled.select`
@@ -37,102 +50,94 @@ type PropTypes = React.PropsWithChildren<{
}) => void;
}>;
-export class ControlPanel extends PureComponent
{
- static defaultProps: PropTypes = {
+/**
+ * Shows the example selection dropdown and some additional information
+ */
+export const ControlPanel: React.FC = (props: PropTypes) => {
+ props = {
examples: {},
droppedFile: null,
selectedExample: null,
selectedLoader: null,
- onExampleChange: () => {}
+ onExampleChange: () => {},
+ ...props
};
- _autoSelected: boolean;
-
- constructor(props) {
- super(props);
- this._autoSelected = false;
+ return (
+
+
+
+ {props.children}
+ Table Schema
+
+
+ );
+};
+
+/**
+ * Shows the selected example in bold font
+ */
+const ExampleHeader: React.FC = ({selectedLoader, selectedExample}) => {
+ if (!selectedLoader || !selectedExample) {
+ return null;
}
- componentDidMount() {
- const {examples = {}, onExampleChange} = this.props;
-
- let selectedLoader = this.props.selectedLoader;
- let selectedExample = this.props.selectedExample;
-
- if ((!selectedLoader || !selectedExample) && !this._autoSelected) {
- selectedLoader = INITIAL_LOADER_NAME;
- selectedExample = examples[selectedLoader][INITIAL_EXAMPLE_NAME];
- this._autoSelected = true;
- }
-
- if (selectedLoader && selectedExample) {
- const example = examples[selectedLoader][selectedExample];
- onExampleChange({selectedLoader, selectedExample, example});
- }
+ return (
+
+
+ {selectedExample} {selectedLoader}{' '}
+
+
+ );
+};
+
+/**
+ * Dropdown that lets user select a new example
+ */
+const ExampleDropDown: React.FC = ({
+ examples = {},
+ selectedLoader,
+ selectedExample,
+ onExampleChange
+}) => {
+ if (!selectedLoader || !selectedExample) {
+ return false;
}
- _renderDropDown() {
- const {examples = {}, selectedLoader, selectedExample, onExampleChange} = this.props;
-
- if (!selectedLoader || !selectedExample) {
- return false;
- }
-
- const selectedValue = `${selectedLoader}.${selectedExample}`;
-
- return (
- {
- const loaderExample = evt.target.value as string;
- const value = loaderExample.split('.');
- const loaderName = value[0];
- const exampleName = value[1];
- const example = examples[loaderName][exampleName];
- onExampleChange({selectedLoader: loaderName, selectedExample: exampleName, example});
- }}
- >
- {Object.keys(examples).map((loaderName, loaderIndex) => {
- const loaderExamples = examples[loaderName];
- return (
-
- );
- })}
-
- );
- }
-
- _renderHeader() {
- const {selectedLoader, selectedExample} = this.props;
- if (!selectedLoader || !selectedExample) {
- return null;
- }
-
- return (
-
-
- {selectedExample} {selectedLoader}{' '}
-
-
- );
- }
-
- render() {
- return (
-
- {this._renderHeader()}
- {this._renderDropDown()}
- {this.props.children}
-
- );
- }
-}
+ const selectedValue = `${selectedLoader}.${selectedExample}`;
+
+ return (
+ {
+ const loaderExample = evt.target.value as string;
+ const value = loaderExample.split('.');
+ const loaderName = value[0];
+ const exampleName = value[1];
+ const example = examples[loaderName][exampleName];
+ onExampleChange({selectedLoader: loaderName, selectedExample: exampleName, example});
+ }}
+ >
+ {Object.keys(examples).map((loaderName, loaderIndex) => {
+ const loaderExamples = examples[loaderName];
+ return (
+
+ );
+ })}
+
+ );
+};
diff --git a/examples/website/geospatial/examples.ts b/examples/website/geospatial/examples.ts
index fa485c8f3e..17162275f0 100644
--- a/examples/website/geospatial/examples.ts
+++ b/examples/website/geospatial/examples.ts
@@ -3,6 +3,7 @@
export const INITIAL_LOADER_NAME = 'GeoParquet';
export const INITIAL_EXAMPLE_NAME = 'Airports';
+
// export const INITIAL_LOADER_NAME = 'GeoJSON';
// export const INITIAL_EXAMPLE_NAME = 'Vancouver';
@@ -30,9 +31,16 @@ export const LOADERS_URL = 'https://raw.githubusercontent.com/visgl/loaders.gl/m
const DECKGL_DATA_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master';
const PARQUET_PATH = '/formats/geoparquet';
-const GEOARROW_TEST_DATA = `${LOADERS_URL}/modules/arrow/test/data`; // geoarrow
+const GEOARROW_TEST_DATA = `${LOADERS_URL}/modules/arrow/test/data/geoarrow`;
export const EXAMPLES: Record> = {
+ GeoArrow: {
+ multipolygon_hole: {
+ format: 'geoarrow',
+ data: `${GEOARROW_TEST_DATA}/multipolygon_hole.arrow`,
+ viewState: {...VIEW_STATE, longitude: 0, latitude: 0, zoom: 4}
+ }
+ },
GeoParquet: {
Airports: {
format: 'geoparquet',
diff --git a/examples/website/geospatial/package.json b/examples/website/geospatial/package.json
index 8e226c0c94..d15c1702e1 100644
--- a/examples/website/geospatial/package.json
+++ b/examples/website/geospatial/package.json
@@ -18,11 +18,12 @@
"@loaders.gl/flatgeobuf": "^4.0.0",
"@loaders.gl/geopackage": "^4.0.0",
"@loaders.gl/parquet": "^4.0.0",
+ "@monaco-editor/react": "^4.5.0",
"mapbox-gl": "npm:empty-npm-package@^1.0.0",
"maplibre-gl": "^2.4.0",
"node-stdlib-browser": "^1.2.0",
- "react": "^18.0.0",
- "react-dom": "^18.0.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
"react-map-gl": "^7.0.0",
"styled-components": "^4.2.0"
},
diff --git a/examples/website/geospatial/tsconfig.json b/examples/website/geospatial/tsconfig.json
new file mode 100644
index 0000000000..a7ca1cd93a
--- /dev/null
+++ b/examples/website/geospatial/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "include": [".", "../../../modules/*/src"],
+ "compilerOptions": {
+ "root": ["."],
+ // "target": "ESNext",
+ // "useDefineForClassFields": true,
+ // "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ // "allowJs": false,
+ // "skipLibCheck": false,
+ // "esModuleInterop": false,
+ // "allowSyntheticDefaultImports": true,
+ // "strict": true,
+ // "forceConsistentCasingInFileNames": true,
+ // "module": "ESNext",
+ // "moduleResolution": "Node",
+ // "resolveJsonModule": true,
+ // "isolatedModules": true,
+ // "noEmit": true,
+ "jsx": "react-jsx"
+ }
+}
diff --git a/examples/website/geospatial/vite.config.ts b/examples/website/geospatial/vite.config.ts
index 7cffe37aeb..45efbade4e 100644
--- a/examples/website/geospatial/vite.config.ts
+++ b/examples/website/geospatial/vite.config.ts
@@ -8,7 +8,7 @@ const getAliases = async (frameworkName, frameworkRootDir) => {
modules.forEach((module) => {
aliases[`${frameworkName}/${module}`] = `${frameworkRootDir}/modules/${module}/src`;
});
- console.log(aliases);
+ // console.log(aliases);
return aliases;
};
diff --git a/examples/website/tiles/tsconfig.json b/examples/website/tiles/tsconfig.json
index a7ca1cd93a..17f90b19a6 100644
--- a/examples/website/tiles/tsconfig.json
+++ b/examples/website/tiles/tsconfig.json
@@ -2,20 +2,6 @@
"include": [".", "../../../modules/*/src"],
"compilerOptions": {
"root": ["."],
- // "target": "ESNext",
- // "useDefineForClassFields": true,
- // "lib": ["DOM", "DOM.Iterable", "ESNext"],
- // "allowJs": false,
- // "skipLibCheck": false,
- // "esModuleInterop": false,
- // "allowSyntheticDefaultImports": true,
- // "strict": true,
- // "forceConsistentCasingInFileNames": true,
- // "module": "ESNext",
- // "moduleResolution": "Node",
- // "resolveJsonModule": true,
- // "isolatedModules": true,
- // "noEmit": true,
"jsx": "react-jsx"
}
}
diff --git a/modules/arrow/src/parsers/parse-arrow-sync.ts b/modules/arrow/src/parsers/parse-arrow-sync.ts
index f6a71f1419..43c585d88c 100644
--- a/modules/arrow/src/parsers/parse-arrow-sync.ts
+++ b/modules/arrow/src/parsers/parse-arrow-sync.ts
@@ -6,6 +6,7 @@ import type {ArrowTable} from '../lib/arrow-table';
import {convertTable} from '@loaders.gl/schema';
import * as arrow from 'apache-arrow';
import {convertArrowToColumnarTable} from '../tables/convert-arrow-to-columnar-table';
+import {serializeArrowSchema} from '../schema/convert-arrow-schema';
// Parses arrow to a columnar table
export function parseArrowSync(
@@ -13,7 +14,11 @@ export function parseArrowSync(
options?: {shape?: 'arrow-table' | 'columnar-table' | 'object-row-table' | 'array-row-table'}
): ArrowTable | ColumnarTable | ObjectRowTable | ArrayRowTable {
const apacheArrowTable = arrow.tableFromIPC([new Uint8Array(arrayBuffer)]);
- const arrowTable: ArrowTable = {shape: 'arrow-table', data: apacheArrowTable};
+ const arrowTable: ArrowTable = {
+ shape: 'arrow-table',
+ schema: serializeArrowSchema(apacheArrowTable.schema),
+ data: apacheArrowTable
+ };
const shape = options?.shape || 'arrow-table';
switch (shape) {
diff --git a/modules/arrow/src/tables/convert-arrow-to-columnar-table.ts b/modules/arrow/src/tables/convert-arrow-to-columnar-table.ts
index c9d609765c..f52e269ac0 100644
--- a/modules/arrow/src/tables/convert-arrow-to-columnar-table.ts
+++ b/modules/arrow/src/tables/convert-arrow-to-columnar-table.ts
@@ -24,6 +24,7 @@ export function convertArrowToColumnarTable(table: ArrowTable): ColumnarTable {
return {
shape: 'columnar-table',
+ schema: table.schema,
data: columnarTable
};
}
diff --git a/modules/arrow/src/tables/convert-arrow-to-geojson-table.ts b/modules/arrow/src/tables/convert-arrow-to-geojson-table.ts
index 5b82d9797e..7fdf8adb43 100644
--- a/modules/arrow/src/tables/convert-arrow-to-geojson-table.ts
+++ b/modules/arrow/src/tables/convert-arrow-to-geojson-table.ts
@@ -16,6 +16,7 @@ import {getGeometryColumnsFromSchema} from '@loaders.gl/gis';
export function convertApacheArrowToArrowTable(arrowTable: arrow.Table): ArrowTable {
return {
shape: 'arrow-table',
+ schema: serializeArrowSchema(arrowTable.schema),
data: arrowTable
};
}
@@ -58,6 +59,7 @@ export function convertArrowToGeoJSONTable(table: ArrowTable): GeoJSONTable {
return {
shape: 'geojson-table',
type: 'FeatureCollection',
+ schema: table.schema,
features
};
}
diff --git a/modules/arrow/src/tables/convert-columnar-to-row-table.ts b/modules/arrow/src/tables/convert-columnar-to-row-table.ts
index f0d0fb841f..ad49de2c27 100644
--- a/modules/arrow/src/tables/convert-columnar-to-row-table.ts
+++ b/modules/arrow/src/tables/convert-columnar-to-row-table.ts
@@ -24,6 +24,7 @@ export function convertColumnarToRowFormatTable(columnarTable: ColumnarTable): O
return {
shape: 'object-row-table',
+ schema: columnarTable.schema,
data: rowFormatTable
};
}
diff --git a/modules/draco/src/draco-loader.ts b/modules/draco/src/draco-loader.ts
index d4c514f701..7b5317c828 100644
--- a/modules/draco/src/draco-loader.ts
+++ b/modules/draco/src/draco-loader.ts
@@ -1,5 +1,6 @@
// loaders.gl, MIT license
// Copyright (c) vis.gl contributors
+
import type {Loader, LoaderOptions} from '@loaders.gl/loader-utils';
import type {DracoMesh} from './lib/draco-types';
import type {DracoParseOptions} from './lib/draco-parser';
@@ -15,15 +16,6 @@ export type DracoLoaderOptions = LoaderOptions & {
};
};
-const DEFAULT_DRACO_OPTIONS: DracoLoaderOptions = {
- draco: {
- decoderType: typeof WebAssembly === 'object' ? 'wasm' : 'js', // 'js' for IE11
- libraryPath: 'libs/',
- extraAttributes: {},
- attributeNameEntry: undefined
- }
-};
-
/**
* Worker loader for Draco3D compressed geometries
*/
@@ -38,5 +30,12 @@ export const DracoLoader: Loader = {
mimeTypes: ['application/octet-stream'],
binary: true,
tests: ['DRACO'],
- options: DEFAULT_DRACO_OPTIONS
+ options: {
+ draco: {
+ decoderType: typeof WebAssembly === 'object' ? 'wasm' : 'js', // 'js' for IE11
+ libraryPath: 'libs/',
+ extraAttributes: {},
+ attributeNameEntry: undefined
+ }
+ }
};
diff --git a/modules/gis/src/lib/tables/convert-table-to-geojson.ts b/modules/gis/src/lib/tables/convert-table-to-geojson.ts
index b41482f4d8..a5ae1105c0 100644
--- a/modules/gis/src/lib/tables/convert-table-to-geojson.ts
+++ b/modules/gis/src/lib/tables/convert-table-to-geojson.ts
@@ -51,7 +51,7 @@ function parseGeometry(
? geometry.buffer.slice(geometry.byteOffset, geometry.byteOffset + geometry.byteLength)
: (geometry as ArrayBuffer);
const geojson = wkbLoader?.parseSync?.(arrayBuffer, {
- wkb: {shape: 'geometry'}
+ wkb: {shape: 'geojson-geometry'}
}) as unknown as Geometry;
return geojson; // binaryGeometry ? binaryToGeometry(binaryGeometry) : null;
// const binaryGeometry = WKBLoader.parseSync?.(geometry);
diff --git a/modules/wkt/test/twkb-loader.spec.ts b/modules/wkt/test/twkb-loader.spec.ts
index 974f252b2c..2b694677c0 100644
--- a/modules/wkt/test/twkb-loader.spec.ts
+++ b/modules/wkt/test/twkb-loader.spec.ts
@@ -51,7 +51,7 @@ test('TWKBLoader#2D', async (t) => {
// }
// if (testCase.wkbXdr && testCase.binary && testCase.geoJSON) {
-// t.deepEqual(parseSync(testCase.twkbXdr, TWKBLoader, {wkb: {shape: 'geometry'}}), testCase.geoJSON);
+// t.deepEqual(parseSync(testCase.twkbXdr, TWKBLoader, {wkb: {shape: 'geojson-geometry'}}), testCase.geoJSON);
// }
// }
diff --git a/website/src/examples-sidebar.js b/website/src/examples-sidebar.js
index 50f71d0c8e..881ba914f4 100644
--- a/website/src/examples-sidebar.js
+++ b/website/src/examples-sidebar.js
@@ -19,6 +19,7 @@
type: 'category',
label: 'Geospatial Loaders',
items: [
+ 'geoarrow',
'geoparquet',
// 'geopackage', sql.js bundling issue...
'flatgeobuf',
diff --git a/website/src/examples/geoarrow.mdx b/website/src/examples/geoarrow.mdx
new file mode 100644
index 0000000000..03dee21b48
--- /dev/null
+++ b/website/src/examples/geoarrow.mdx
@@ -0,0 +1,7 @@
+# GeoArrow
+
+import Demo from 'examples/website/geospatial/app';
+
+
+
+
diff --git a/website/static/images/examples/geoarrow.jpg b/website/static/images/examples/geoarrow.jpg
new file mode 100644
index 0000000000..bd9953a6d1
Binary files /dev/null and b/website/static/images/examples/geoarrow.jpg differ