This project was bootstrapped with Create React App.
This application renders three predefined layers on top of a maplibre
base map using deck.gl
and @deck.gl/carto
.
To simplify the functionality, the datasets are predefined and requests are launched when the application is initialized. When the dataset info is received by the asynchronous request a layer is created.
The datasets preconfigured are:
carto-demo-data.demo_tilesets.sociodemographics_usa_blockgroup
carto-demo-data.demo_tables.retail_stores
carto-demo-data.demo_tables.airports
There are two types of layers:
- query: Render the default
CartoLayer
's point layer. - tilesets: Render a tileset layer.
Extra credit: As layers are added to the layerOrder
list as the fetchDataset
response is received, some layers can overlap the others. A visibility button (represented with an eye icon) has been added to each layer card to hide/show individually.
The following point
layer's parameters can be configured:
- Fill color
- Outline color
- Outline size
- Radius
Note: tileset
layers cannot be customized as is out of the scope of this test application.
Folder | Description |
---|---|
actions | Action creators and actionType definition |
api | Functions for HTTP requests |
components | UI components |
config | Static configuration modules |
context | Context definitions i.e. mapLayers that contains the app global state |
fixtures | Static mocked data |
hooks | Common hooks reusable by the components |
reducers | Global state's reducer functions |
services | Utility functions |
translators | Utility function specialized in converting objects from one type to another |
Folder | Description |
---|---|
index.tsx | Attaches React root node to the DOM and add some global wrappers |
App.tsx | Application main component with the initialization requests |
Layout.tsx | Layout component composed of a navigation side bar and the app content |
SidePanel.tsx | Control side panel |
LayersPanel.tsx | Display the layers shown in the map in renderization order |
MapContainer.tsx | Responsible of showing the base map and creating the layers objects passed to deck.gl |
There are state dependencies between components that make necesssary a global state management solution.
This application uses React Context because the overhead added by the Redux boilerplate is too much for a small application. The tradeoff is the performance penalty added by React Context, although is not appreciated in this application, it should be considered for enterprise-grade applications where Redux is more suitable.
Although React Context is used, the architecture has been defined similarly to the Redux architecture, using actions
and reducers
, which would simplify the migration in the case that is required.
The current state
and action
's dispatcher are accessible by a dedicated hook called useMapLayers
.
All the state management logic in the mapLayersReducer
allows being reused by the different components, having specialized rendering components.
All state changes are driven by actions that are thrown from the components. Action types are defined in the actions/actionTypes.ts
file.
The state model has been defined with the following structure:
export interface MapState {
datasets: Record<string, Dataset>;
layers: Record<string, LayerConfig>;
layerVisConfigs: Record<string, LayerVisConfig>;
layerOrder: string[];
layerCounter: number;
}
Field name | Type | Description |
---|---|---|
datasets | Record<string, Dataset> | Indexed by dataset id storing data relative to the dataset |
layers | Record<string, LayerConfig> | Indexed by layer unique id, basic layer configuration |
layerVisConfigs | Record<string, LayerVisConfig> | Indexed by layer unique id, visualization configuration as fill color, point radius, visibility, etc. |
layerOrder | string [] | Layers rendering order where the first position is the layer rendered on top |
layerCounter | number | Count of the layers created used to create a basic UUID generation function |
datasets
, layers
, and layerVisConfigs
has been modeled as an object indexed by a dataset or layer id
. The other options analyzed were using an Array
or a Map
. Finally, data is stored in plain objects because is serializable and optimizes the read/remove operations. Map
was discarded because in case of implementing persistence, it makes harder the serialization in the local storage or an external service.
axios
is used to request data to the API as is reduces the boilerplate code compared to the native window.fetch
. Not implemented for this example but to considered in a real application is the use of axios
source tokens to cancel on-going requests.
API is accesed from the actions, abstracting the components from this matter.
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Note: this is a one-way operation. Once you eject
, you can’t go back!
If you aren’t satisfied with the build tool and configuration choices, you can eject
at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it..