diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index fb85e75ef08..14e4b00d829 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -97,6 +97,15 @@ const layer = new CartoLayer({ }); ``` +### loaders.gl + +loaders.gl dependencies are updated to v4. Although most version differences are handled internal to deck.gl, some changes may be required for applications that work directly with loaders: + +- If an application imports `@loaders.gl/*` sub packages to load specific data formats, they should be upgraded from v3.x to v4.x. +- If the layer prop `dataTransform` is used to pre-process data, the loaded data object might have changed. For example, `CSVLoader` now yields [a new table format](https://loaders.gl/docs/specifications/category-table). +- For a complete list of breaking changes and improvements, see [loaders.gl 4.0 upgrade guide](https://loaders.gl/docs/upgrade-guide#upgrading-to-v40). + + ## Upgrading from deck.gl v8.8 to v8.9 #### Breaking changes diff --git a/modules/core/src/lifecycle/prop-types.ts b/modules/core/src/lifecycle/prop-types.ts index 9df40ff779f..609a066263a 100644 --- a/modules/core/src/lifecycle/prop-types.ts +++ b/modules/core/src/lifecycle/prop-types.ts @@ -192,8 +192,22 @@ const TYPE_DEFINITIONS = { }, data: { transform: (value, propType: DataPropType, component) => { + if (!value) { + return value; + } const {dataTransform} = component.props; - return dataTransform && value ? dataTransform(value) : value; + if (dataTransform) { + return dataTransform(value); + } + // Detect loaders.gl v4 table format + if ( + typeof value.shape === 'string' && + value.shape.endsWith('-table') && + Array.isArray(value.data) + ) { + return value.data; + } + return value; } }, image: { diff --git a/test/data/bart-stations.csv b/test/data/bart-stations.csv new file mode 100644 index 00000000000..bf909040b22 --- /dev/null +++ b/test/data/bart-stations.csv @@ -0,0 +1,45 @@ +name,code,longitude,latitude,entries,exits +"Lafayette (LAFY)",LF,-122.123801,37.893394,3481,3616 +"12th St. Oakland City Center (12TH)",12,-122.271604,37.803664,13418,13547 +"16th St. Mission (16TH)",16,-122.419694,37.765062,12409,12351 +"19th St. Oakland (19TH)",19,-122.269029,37.80787,13108,13090 +"24th St. Mission (24TH)",24,-122.418466,37.752254,12817,12529 +"Ashby (ASHB)",AS,-122.26978,37.853024,5452,5341 +"Balboa Park (BALB)",BP,-122.447414,37.721981,11170,9817 +"Bay Fair (BAYF)",BF,-122.126871,37.697185,5564,5516 +"Castro Valley (CAST)",CV,-122.075567,37.690754,2781,2735 +"Civic Center/UN Plaza (CIVC)",CC,-122.413756,37.779528,24798,22626 +"Colma (COLM)",CM,-122.466233,37.684638,4397,4214 +"Coliseum/Oakland Airport (COLS)",CL,-122.197273,37.754006,5837,5902 +"Concord (CONC)",CN,-122.029095,37.973737,6035,6008 +"Daly City (DALY)",DC,-122.469081,37.706121,8681,8502 +"Downtown Berkeley (DBRK)",BK,-122.268045,37.869867,11043,11762 +"El Cerrito del Norte (DELN)",EN,-122.317269,37.925655,8176,8668 +"Dublin/Pleasanton (DUBL)",ED,-121.900367,37.701695,7702,7554 +"Embarcadero (EMBR)",EM,-122.396742,37.792976,40376,46951 +"Fremont (FRMT)",FM,-121.9764,37.557355,8748,8673 +"Fruitvale (FTVL)",FV,-122.224274,37.774963,7701,8012 +"Glen Park (GLEN)",GP,-122.434092,37.732921,7732,7072 +"Hayward (HAYW)",HY,-122.087967,37.670399,4958,5003 +"Lake Merritt (LAKE)",LM,-122.265609,37.797484,6539,6604 +"MacArthur (MCAR)",MA,-122.267227,37.828415,9000,9228 +"Millbrae (MLBR)",MB,-122.38666,37.599787,6570,6149 +"Montgomery St. (MONT)",MT,-122.401407,37.789256,43430,45128 +"North Berkeley (NBRK)",NB,-122.283451,37.87404,4363,4563 +"North Concord/Martinez (NCON)",NC,-122.024597,38.003275,2800,2652 +"Orinda (ORIN)",OR,-122.183791,37.878361,2896,2970 +"Pleasant Hill/Contra Costa Centre (PHIL)",PH,-122.056013,37.928403,7574,7442 +"Pittsburg/Bay Point (PITT)",WP,-121.945154,38.018914,6262,6343 +"El Cerrito Plaza (PLZA)",EP,-122.299272,37.903059,4763,4952 +"Powell St. (POWL)",PL,-122.406857,37.784991,29460,25621 +"Richmond (RICH)",RM,-122.353165,37.936887,4184,4029 +"Rockridge (ROCK)",RR,-122.251793,37.844601,5299,5775 +"San Leandro (SANL)",SL,-122.161311,37.722619,5836,5921 +"San Bruno (SBRN)",SB,-122.416038,37.637753,3628,3634 +"San Francisco Int'l Airport (SFIA)",SO,-122.392612,37.616035,5833,4904 +"South Hayward (SHAY)",SH,-122.057551,37.6348,3007,2829 +"South San Francisco (SSAN)",SS,-122.444116,37.664174,3542,3441 +"Union City (UCTY)",UC,-122.017867,37.591208,4772,4770 +"Walnut Creek (WCRK)",WC,-122.067423,37.905628,6719,6917 +"West Dublin/Pleasanton (WDUB)",WD,-121.928099,37.699759,3303,3447 +"West Oakland (WOAK)",OW,-122.294582,37.804675,7312,6838 \ No newline at end of file diff --git a/test/modules/core/lifecycle/component-state.spec.ts b/test/modules/core/lifecycle/component-state.spec.ts index 1a3d9e0039c..0d56820c905 100644 --- a/test/modules/core/lifecycle/component-state.spec.ts +++ b/test/modules/core/lifecycle/component-state.spec.ts @@ -3,6 +3,8 @@ import test from 'tape-promise/tape'; import {_ComponentState as ComponentState, _Component as Component} from '@deck.gl/core'; import {device} from '@deck.gl/test-utils'; +import {load} from '@loaders.gl/core'; +import {CSVLoader} from '@loaders.gl/csv'; const EMPTY_ARRAY = Object.freeze([]); @@ -15,7 +17,7 @@ type TestComponentProps = { const defaultProps = { // data: Special handling for null, see below data: {type: 'data', value: EMPTY_ARRAY, async: true}, - dataTransform: data => data, + dataTransform: null, image: {type: 'image', value: null, async: true} }; @@ -192,8 +194,7 @@ test('ComponentState#asynchronous async props', async t => { t.end(); }); -// TODO - disabled for v9 -test('ComponentState#async props with transform', t => { +test('ComponentState#async props with transform', async t => { const testContext = {device}; const testData = [0, 1, 2, 3, 4]; @@ -205,10 +206,11 @@ test('ComponentState#async props with transform', t => { 0, 0, 255, 255 ]), width: 2, height: 2}; + // @ts-expect-error const state = new ComponentState(); // Simulate Layer class - const makeComponent = (props: Record, onAsyncPropUpdated = () => {}) => { + const makeComponent = (props: Record) => { const comp = new TestComponent(props); // @ts-expect-error comp.internalState = state; @@ -217,7 +219,6 @@ test('ComponentState#async props with transform', t => { state.component = comp; state.setAsyncProps(comp.props); - state.onAsyncPropUpdated = onAsyncPropUpdated; return comp; }; @@ -251,32 +252,34 @@ test('ComponentState#async props with transform', t => { t.is(component.props.image, image, 'Unchanged image value is not transformed again'); // Async value for async prop - component = makeComponent( - { - data: Promise.resolve(testData), - dataTransform: d => d.slice(0, 2), - image: Promise.resolve(testImage) - }, - // @ts-expect-error - (propName, value) => { - if (propName === 'image') { - t.ok(image.destroyed, 'Last texture is deleted'); - image = component.props.image; - t.ok(image, 'Async value for image should be transformed'); - } - if (propName === 'data') { - data = component.props.data; - t.deepEqual(data, [0, 1], 'Async value for data should be transformed'); - } - - // @ts-expect-error - if (!state.isAsyncPropLoading()) { - // @ts-expect-error - state.finalize(); - t.ok(image.destroyed, 'Texture is deleted on finalization'); - - t.end(); - } - } - ); + const testDataAsync = Promise.resolve(testData); + const testImageAsync = Promise.resolve(testImage); + component = makeComponent({ + data: testDataAsync, + dataTransform: d => d.slice(0, 2), + image: testImageAsync + }); + + await testDataAsync; + data = component.props.data; + t.deepEqual(data, [0, 1], 'Async value for data should be transformed'); + + await testImageAsync; + t.ok(image.destroyed, 'Last texture is deleted'); + image = component.props.image; + t.ok(image, 'Async value for image should be transformed'); + + const loadDataAsync = load('./test/data/bart-stations.csv', [CSVLoader]); + component = makeComponent({ + data: loadDataAsync, + image: testImageAsync + }); + + await loadDataAsync; + t.is(component.props.image, image, 'Unchanged image value is not transformed again'); + data = component.props.data; + t.ok(Array.isArray(data), 'loaders.gl table object is properly transformed'); + + state.finalize(); + t.ok(image.destroyed, 'Texture is deleted on finalization'); });