Skip to content

Commit

Permalink
Merge 07d4127 into 5e504fd
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Sep 11, 2019
2 parents 5e504fd + 07d4127 commit b4f9692
Show file tree
Hide file tree
Showing 22 changed files with 177 additions and 167 deletions.
4 changes: 2 additions & 2 deletions docs/api-reference/json/json-converter.md
Expand Up @@ -13,7 +13,7 @@ Requirements on the JSON description:
## Usage

```js
import {_JSONConverter as JSONConverter} from '@deck.gl/json';
import {JSONConverter} from '@deck.gl/json';

import json from './us-map.json';

Expand All @@ -28,7 +28,7 @@ const deck = new Deck({
json
});

deck.setProps(jsonConverter.convertJSONToDeckProps(json));
deck.setProps(jsonConverter.convertJSON(json));
```


Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/json/overview.md
Expand Up @@ -40,7 +40,7 @@ The module was created to enable specifying deck.gl visualizations using [JSON f
<script src="https://unpkg.com/@deck.gl/json@^7.0.0/dist.min.js"></script>
<!-- usage -->
<script type="text/javascript">
const {_JSONConverter} = deck;
const {JSONConverter} = deck;
</script>
```

Expand Down
3 changes: 1 addition & 2 deletions docs/whats-new.md
Expand Up @@ -8,8 +8,7 @@ This page contains highlights of each deck.gl release. Also check our [vis.gl bl

The `JSONConverter` class has been generalized and can now be used independently of deck.gl to "hydrate" JavaScript from JSON text specifications. This supports its use a foundation technology for providing non-JavaScript bindings such as `pydeck`.

This has caused some breaking changes to this experimental module. For details and work-arounds see the upgrade guide:
- The `JSONLayer` is no longer exported from `@deck.gl/json`.
This has caused some breaking changes to this experimental module. For details and work-arounds see the upgrade guide. Most notably, the `JSONLayer` is no longer exported from `@deck.gl/json`.

## deck.gl v7.2

Expand Down
33 changes: 21 additions & 12 deletions examples/experimental/json-layer/json-layer/json-layer.js
@@ -1,7 +1,21 @@
// This layer allows any deck.gl app to support user-defined JSON layers
import {CompositeLayer} from '@deck.gl/core';
// import DeckJSONConverter from '../deck-json-converter/deck-json-converter';
import DeckJSONConfiguration from '../../json-browser/deck-json-converter/deck-json-configuration';
import {JSONConverter, JSONConfiguration} from '@deck.gl/json';

import {
MapView,
FirstPersonView,
OrbitView,
OrthographicView,
COORDINATE_SYSTEM
} from '@deck.gl/core';
import * as layers from '@deck.gl/layers';
import GL from '@luma.gl/constants';

const DEFAULT_CONFIGURATION = {
classes: Object.assign({MapView, FirstPersonView, OrbitView, OrthographicView}, layers),
enumerations: {COORDINATE_SYSTEM, GL}
};

const defaultProps = {
// Accepts JSON strings by parsing them to JSON, naturally
Expand All @@ -12,22 +26,17 @@ const defaultProps = {
export default class JSONLayer extends CompositeLayer {
initializeState() {
this.state = {
converter: null, // new DeckJSONConverter()
layers: [],
configuration: {}
converter: new JSONConverter({
configuration: new JSONConfiguration(DEFAULT_CONFIGURATION, this.props.configuration)
}),
layers: []
};
}

updateState({props, oldProps, changeFlags}) {
if (props.configuration !== oldProps.configuration) {
this.setState({
configuration: new DeckJSONConfiguration(this.props.configuration)
});
}

const layersChanged = changeFlags.dataChanged || props.configuration !== oldProps.configuration;
if (layersChanged) {
this.state.layers = []; // getJSONLayers(props.data, this.state.configuration);
this.state.layers = this.state.converter.convertJson(props.data);
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/experimental/json-layer/json-layer/json-layer.md
Expand Up @@ -6,7 +6,7 @@ A composite layer that parses a json array and instantiates deck.gl layers by ma
## Usage

```js
import {_JSONLayer as JSONLayer} from '@deck.gl/json';
import {JSONLayer} from '@deck.gl/json';

const layers = [
new JSONLayer({
Expand Down
@@ -1,7 +1,6 @@
import test from 'tape-catch';
import {_JSONLayer as JSONLayer} from '@deck.gl/json';
import {testLayer, testInitializeLayer} from '@deck.gl/test-utils';

import {JSONLayer} from '@deck.gl/json';
import {configuration, JSON_DATA} from '../../../../test/modules/json/deck-json-converter.spec';

test('JSONLayer#import', t => {
Expand Down
12 changes: 9 additions & 3 deletions examples/playground/src/app.js
@@ -1,9 +1,10 @@
import React, {Component} from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import DeckWithMaps from './deck-with-maps';
import {StaticMap} from 'react-map-gl';
import DeckWithMaps from './deck-with-maps';

import JSONConverter from './deck-json-converter/deck-json-converter';
import {JSONConverter, JSONConfiguration} from '@deck.gl/json';
import {DECK_JSON_CONVERTER_CONFIGURATION} from './deck-json-converter';

import AceEditor from 'react-ace';
import 'brace/mode/json';
Expand Down Expand Up @@ -84,7 +85,12 @@ export class App extends Component {
this._onTemplateChange = this._onTemplateChange.bind(this);
this._onEditorChange = this._onEditorChange.bind(this);

this.jsonConverter = new JSONConverter({configuration: JSON_CONFIGURATION});
// Configure and create the JSON converter instance
const configuration = new JSONConfiguration(
DECK_JSON_CONVERTER_CONFIGURATION,
JSON_CONFIGURATION
);
this.jsonConverter = new JSONConverter({configuration});
}

componentDidMount() {
Expand Down
@@ -1,5 +1,6 @@
// TODO - This file contains too much custom code, should just require a clean `JSONConfiguration`.

// Converts JSON to props ("hydrating" classes, resolving enums and functions etc).
// TODO - Currently converts in place, might be clearer to convert to separate structure

// Converts a JSON payload to a deck.gl props object
// Lightly processes `json` props, transform string values, and extract `views` and `layers`
Expand All @@ -11,28 +12,20 @@
// * Optionally, error checking could be applied, but ideally should leverage
// non-JSON specific mechanisms like prop types.

import {_JSONConverter, _shallowEqualObjects, _parseExpressionString} from '@deck.gl/json';
import {_shallowEqualObjects, _parseExpressionString} from '@deck.gl/json';
import {MapView, FirstPersonView, OrbitView, OrthographicView} from '@deck.gl/core';
import {COORDINATE_SYSTEM} from '@deck.gl/core';
import GL from '@luma.gl/constants';
import enhancedFetch from './enhanced-fetch';

import DeckJSONConfiguration from './deck-json-configuration';

export const DEFAULT_MAP_PROPS = {style: 'mapbox://styles/mapbox/light-v9'};

export default class DeckJSONConverter extends _JSONConverter {
constructor(props) {
super({
...props,
// Inject default deck configuration
configuration: new DeckJSONConfiguration(props.configuration)
});
this.configuration.preProcessClassProps = this.preProcessClassProps.bind(this);
}

preProcessClassProps(Class, props) {
export const DECK_JSON_CONVERTER_CONFIGURATION = {
// Support all `@deck.gl/core` Views by default
classes: {MapView, FirstPersonView, OrbitView, OrthographicView},
enumerations: {COORDINATE_SYSTEM, GL},
preProcessClassProps(Class, props, configuration) {
props.fetch = props.fetch || enhancedFetch;
return getJSONLayerProps(Class, props, this.configuration);
}

return convertFunctions(Class, props, configuration);
},
postProcessConvertedJson(json) {
// Handle `json.initialViewState`
// If we receive new JSON we need to decide if we should update current view state
Expand All @@ -50,68 +43,14 @@ export default class DeckJSONConverter extends _JSONConverter {
delete json.initialViewState;
}

normalizeMapProps(json, this.configuration);

return json;
}
}

/*
// Converts JSON to props ("hydrating" classes, resolving enums and functions etc).
export function convertDeckJSON(json, configuration) {
const jsonProps = convertJSON(json, configuration);
// Normalize deck props
if ('initialViewState' in jsonProps) {
jsonProps.viewState = jsonProps.viewState || jsonProps.initialViewState;
}
normalizeMapProps(jsonProps, configuration);
return jsonProps;
}
*/

// Normalizes map/mapStyle etc props to a `map: {style}` object-valued prop
function normalizeMapProps(jsonProps, configuration) {
if (jsonProps.map || jsonProps.mapStyle) {
jsonProps.map = Object.assign({}, DEFAULT_MAP_PROPS, jsonProps.map);
}

if (!jsonProps.map) {
return;
}

if ('mapStyle' in jsonProps) {
jsonProps.map.style = jsonProps.mapStyle;
jsonProps.map.mapStyle = jsonProps.mapStyle;
delete jsonProps.mapStyle;
}

// TODO - better map handling
if ('viewState' in jsonProps) {
jsonProps.map.viewState = jsonProps.viewState;
}
}

// LAYERS
/*
// Replaces accessor props
export function getJSONLayers(jsonLayers = [], configuration) {
// assert(Array.isArray(jsonLayers));
const layerCatalog = configuration.layers || {};
return jsonLayers.map(jsonLayer => {
const Layer = layerCatalog[jsonLayer.type];
const props = getJSONLayerProps(Layer, jsonLayer, configuration);
return Layer && new Layer(props);
});
}
*/
};

// TODO - we need to generalize string to function conversion
// and move it upstream into the json-converter
// eslint-disable-next-line complexity
function getJSONLayerProps(Layer, jsonProps, configuration) {
function convertFunctions(Layer, jsonProps, configuration) {
let propTypes = Layer && Layer._propTypes && Layer._propTypes;
// HACK: Trigger generation of propType
if (!propTypes && Layer.defaultProps) {
Expand Down

This file was deleted.

File renamed without changes.
2 changes: 1 addition & 1 deletion examples/playground/webpack.config.js
Expand Up @@ -29,7 +29,7 @@ const CONFIG = {
},

plugins: [
new HtmlWebpackPlugin({title: 'JSON Browser'}),
new HtmlWebpackPlugin({title: 'Playground'}),
// Optional: Enables reading mapbox token from environment variable
new webpack.EnvironmentPlugin(['MapboxAccessToken'])
],
Expand Down
5 changes: 3 additions & 2 deletions modules/json/src/index.js
@@ -1,9 +1,10 @@
// @deck.gl/json: top-level exports

// Generic JSON converter, usable by other wrapper modules
export {default as _JSONConverter} from './lib/json-converter';
export {default as _JSONConfiguration} from './lib/json-configuration';
export {default as JSONConverter} from './lib/json-converter';
export {default as JSONConfiguration} from './lib/json-configuration';

// Helpers
export {default as _convertFunctions} from './lib/helpers/convert-functions';
export {default as _parseExpressionString} from './lib/helpers/parse-expression-string';
export {shallowEqualObjects as _shallowEqualObjects} from './utils/shallow-equal-objects';
37 changes: 37 additions & 0 deletions modules/json/src/lib/helpers/convert-functions.js
@@ -0,0 +1,37 @@
import parseExpressionString from './parse-expression-string';
import {getPropTypes, isFunctionProp} from './deck-prop-types';

// Try to determine if any props are function valued
// and if so convert their string values to functions
export default function convertFunctions(Class, props, configuration) {
const propTypes = getPropTypes(Class);
if (!propTypes) {
return props;
}

// Use deck.gl prop types if available.
return convertFunctionsUsingPropTypes(Class, props, propTypes, configuration);
}

function convertFunctionsUsingPropTypes(Class, props, propTypes, configuration) {
const replacedProps = {};
for (const propName in props) {
let propValue = props[propName];

// Parse string valued expressions
const isFunction = isFunctionProp(propTypes, propName);

// Parse string as "expression", return equivalent JavaScript function
if (isFunction && typeof propValue === 'string') {
const isAccessor = true;
propValue = parseExpressionString(propValue, configuration, isAccessor);
}

// Invalid functions return null, show default value instead.
if (propValue) {
replacedProps[propName] = propValue;
}
}

return replacedProps;
}
31 changes: 31 additions & 0 deletions modules/json/src/lib/helpers/deck-prop-types.js
@@ -0,0 +1,31 @@
// NOTE - This is sniffing for deck.gl prop types
// TODO - We may want to do something similar for React `prop-types`
// TODO - We need something similar for non-deck, non-React classes
// TODO - The deck prop types system could potentially be exported as a general prop types package....

export function getPropTypes(Class) {
let propTypes = Class && Class._propTypes && Class._propTypes;
// HACK: Trigger generation of propTypes
if (!propTypes && Class.defaultProps) {
new Class({}); // eslint-disable-line no-new
propTypes = Class && Class._propTypes && Class._propTypes;
}
return propTypes;
}

export function isFunctionProp(propTypes, propName) {
const propType = propTypes && propTypes[propName];
if (!propType) {
// TODO - simple heuristic if prop types are not avaialable
return propName.startsWith('get');
}

const type = typeof propType === 'object' && propType.type;
switch (type) {
case 'accessor':
case 'function':
return true;
default:
return false;
}
}
10 changes: 6 additions & 4 deletions modules/json/src/lib/helpers/instantiate-class.js
Expand Up @@ -6,9 +6,11 @@ export function instantiateClass(type, props, configuration) {

// Check that the class is in the configuration.
if (!Class && !Component) {
const {log = console} = configuration; // eslint-disable-line
const {log} = configuration; // eslint-disable-line
const stringProps = JSON.stringify(props, null, 0).slice(0, 40);
log.warn(`JSON converter: No registered class of type ${type}(${stringProps}...) `);
if (log) {
log.warn(`JSON converter: No registered class of type ${type}(${stringProps}...) `);
}
return null;
}

Expand All @@ -21,7 +23,7 @@ export function instantiateClass(type, props, configuration) {

function instantiateJavaScriptClass(Class, props, configuration) {
if (configuration.preProcessClassProps) {
props = configuration.preProcessClassProps(Class, props);
props = configuration.preProcessClassProps(Class, props, configuration);
}
return new Class(props);
}
Expand All @@ -31,7 +33,7 @@ function instantiateReactComponent(Component, props, configuration) {
const {children = []} = props;
delete props.children;
if (configuration.preProcessClassProps) {
props = configuration.preProcessClassProps(Component, props);
props = configuration.preProcessClassProps(Component, props, configuration);
}
return React.createElement(Component, props, children);
}

0 comments on commit b4f9692

Please sign in to comment.