Skip to content

Commit 6b029f2

Browse files
authored
Merge pull request #753 from Vizzuality/develop
Develop
2 parents bc7ee41 + 4e83dcb commit 6b029f2

30 files changed

Lines changed: 542 additions & 274 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ It uses github pages and the content is generated from the `_docs` folder.
1313

1414
In the project directory, you can run:
1515

16-
### `yarn dev`
16+
### `pnpm dev`
1717

1818
Runs the app in the development mode.<br>
1919
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
2020

2121
The page will reload if you make edits.<br>
2222
You will also see any lint errors in the console.
2323

24-
### `yarn docs:serve`
24+
### `pnpm docs:serve`
2525

26-
### `yarn test`
26+
### `pnpm test`
2727

2828
Launches the test runner in the interactive watch mode.<br>
2929

30-
### `yarn run build`
30+
### `pnpm run build`
3131

3232
Builds the app for production to the `build` folder.<br>
3333
It correctly bundles React in production mode and optimizes the build for the best performance.
@@ -38,6 +38,6 @@ Your app is ready to be deployed!
3838
See the section about [build](https://vitejs.dev/guide/build) for more information.
3939

4040

41-
### `yarn start`
41+
### `pnpm start`
4242

4343
Runs the preview mode of Vite from the build that has to be run before. It will run on [http://localhost:3000](http://localhost:3000)

_docs/dev/architecture/index.md

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,156 @@ has_toc: true
88
permalink: /_docs/dev/architecture
99
---
1010

11-
architecture intro
11+
## What this app is
12+
13+
Half-Earth v3 is a Vite + React single-page application that renders a 3D ArcGIS SceneView “globe” alongside data-driven scenes and UIs. Redux powers routing and synchronizes UI/globe state into the URL, enabling shareable, reproducible views. Translations, analytics, and content integrations complete the platform.
14+
15+
Key technologies
16+
- React 17, Redux, redux-first-router
17+
- @arcgis/core 4.x (ArcGIS JS API) for the 3D globe
18+
- @loadable/component for page-level code-splitting
19+
- @tanstack/react-query for server-state
20+
- Transifex Native for i18n
21+
- SCSS modules for styles
22+
23+
Entry points
24+
- index.html: loads ArcGIS CSS and bootstraps the app bundle
25+
- src/index.jsx: creates the Redux store and mounts <App />
26+
- src/app.jsx: initializes Transifex + React Query and lazy-loads pages based on the current route
27+
28+
## Code organization (where things live)
29+
30+
- Pages (src/pages/*)
31+
- Top-level routes: data-globe, featured-globe, aoi, nrc (+ mobile variants under src/pages/mobile/*).
32+
- Pages compose a scene with page chrome (headers, sidebars, modals, menus).
33+
- Scenes (src/containers/scenes/*)
34+
- Each scene wraps the ArcGIS SceneView via components/scene and owns per-scene configuration (sceneSettings).
35+
- Orchestrates layers, sidebars, tooltips, onboarding, and other managers.
36+
- Components (src/components/*)
37+
- Reusable presentational components. Notable: components/scene/* (Scene wrapper), tooltips, modals.
38+
- Containers (src/containers/*)
39+
- “Smart” components wired to Redux and ArcGIS.
40+
- Includes layers/*, sidebars/*, menus/*, and managers/* (arcgis-layer-manager, globe-events-manager, zoom-into-geometry-manager, etc.).
41+
- Store (src/store/*)
42+
- store/store.ts and store/reducerRegistry.js implement the Redux store and dynamic reducer injection.
43+
- store/redux-modules/* contains feature modules (actions, reducers, initial state) registered on demand.
44+
- Constants (src/constants/*)
45+
- Layer and scene configuration, slugs, and service URLs (e.g., mol-layers-configs, layers-urls, layers-groups, layers-slugs).
46+
- Utils, hooks, services
47+
- src/utils/*: ArcGIS helpers (layer creation/visibility/order, globe events, state-to-url) and general utilities.
48+
- src/hooks/*: custom hooks (e.g., hooks/esri.js for search/sketch widgets and watch utils).
49+
- src/services/*: service wrappers (e.g., esri-feature-service.ts for querying/editing FeatureLayers).
50+
- Styles (src/styles/*)
51+
- SCSS modules and theme styles (e.g., styles/themes/scene-theme.module.scss).
52+
53+
## Routing and URL-state model
54+
55+
Router
56+
- redux-first-router connectRoutes is configured in src/router.ts.
57+
- Each route defines a path and a page key used by App to lazy-load the right page.
58+
59+
URL serialization
60+
- src/utils/state-to-url encodes/decodes JSON-like state into location.query.
61+
- Main query keys:
62+
- globe: camera center/zoom, activeLayers, basemap, labels, terrain, and globe UI toggles.
63+
- ui: non-globe UI state (sidebars, modals, menus, onboarding, etc.).
64+
- lang: current locale.
65+
66+
Actions
67+
- store/actions/url-actions exposes changeGlobe, changeUI, changeLang.
68+
- Each action updates only its subtree in location.query, preserving the rest.
69+
70+
## State management (high level)
71+
72+
- The Redux store (store/store.ts) composes base reducers and supports dynamic reducer registration through store/reducerRegistry.js.
73+
- Feature modules under store/redux-modules/<feature> export a reduxConfig that reducerRegistry.registerModule consumes to inject reducers at runtime (code-splitting friendly).
74+
- Location (redux-first-router) provides routing and the URL-backed state used by selectors.
75+
- Deep dive: State Management → /_docs/dev/architecture/state-management
76+
77+
## The ArcGIS “globe” architecture
78+
79+
Scene wrapper
80+
- components/scene/scene.js + scene-component.jsx create an ArcGIS Map and a 3D SceneView for each scene.
81+
- Applies per-scene settings (environment, constraints, padding, popup UI) via the sceneSettings prop.
82+
- On mobile, the Scene uses a lower qualityProfile to reduce GPU load.
83+
84+
Camera, rotation, and URL sync
85+
- Optional initial rotation animates the globe on first visit; a flag in localStorage avoids repeating.
86+
- Uses @arcgis/core/core/reactiveUtils (via hooks/esri) to update the URL when the view becomes stationary (center + zoom).
87+
88+
Touch pan refinement
89+
- A custom touch-pan handler in the Scene improves iOS Safari behavior for one-finger panning.
90+
91+
Basemap handling
92+
- utils/layer-manager-utils.setBasemap composes a Basemap from configured base layers (constants/mol-layers-configs + constants/layers-urls).
93+
94+
Layer system overview
95+
- Configuration-driven: constants/mol-layers-configs maps slugs → type (FeatureLayer, TileLayer, VectorTileLayer), URL or portalId, default opacity, optional renderer and bbox.
96+
- Creation/activation: utils/layer-manager-utils.createLayer/addLayerToMap/activateLayersOnLoad and addActiveLayersToScene instantiate and add activeLayers from state.
97+
- Visibility/order/opacity: containers/managers/arcgis-layer-manager calls utils/arcgis-layer-manager-utils.setLayersVisibility (and optionally setLayersOrder) based on activeLayers.
98+
- Labels: containers/layers/labels-layer styles label layers with LabelClass rules from @arcgis/core.
99+
100+
Events and interactions
101+
- containers/managers/globe-events-manager attaches pointer handlers and uses view.hitTest; utils/globe-events-utils centralizes hit results, cursor updates, flyTo helpers, and avatar/marker placement.
102+
- hooks/esri.js exposes search and sketch widget logic, including area validation against configured limits.
103+
104+
Examples by scene
105+
- Data scene (src/containers/scenes/data-scene/*): thematic layers, labels, tooltips, sidebars, and the globes menu. Configured via data-scene-config.js.
106+
- AOI scene (src/containers/scenes/aoi-scene/*): AOI mask/outline, optional terrain exaggeration, basemap selector, and ZoomIntoGeometryManager for camera transitions.
107+
108+
### Per-scene layer configuration files
109+
- Each scene ships with a dedicated config module that defines its initial globe and UI state, especially the list/order of layers.
110+
- Examples:
111+
- src/containers/scenes/data-scene/data-scene-config.js defines globe.activeLayers, zoom/center, padding, environment, popup and basemap.
112+
- src/containers/scenes/aoi-scene/config.js defines activeLayers, padding, environment, constraints and basemap.
113+
- How it flows:
114+
1) The page builds sceneSettings from the scene’s config and passes it to the Scene component (components/scene).
115+
2) On map load, activateLayersOnLoad(map, initialActiveLayers, layersConfig) instantiates those layers by looking up their slug in constants/mol-layers-configs.
116+
3) ArcgisLayerManager keeps visibility/opacity in sync with activeLayers changes.
117+
- This separation lets us evolve per‑scene defaults without touching the global layer registry (constants/mol-layers-configs, constants/layers-urls).
118+
119+
## Data and services
120+
121+
- ArcGIS Online: constants/layers-urls centralize FeatureServer/MapServer URLs and portal item ids per layer slug.
122+
- Service helpers: src/services/esri-feature-service.ts wraps querying/editing via @arcgis/core and @esri/arcgis-rest-feature-layer.
123+
124+
### Contentful content and translations
125+
- What we store there
126+
- Layer metadata (descriptions, sources, credits) used across the UI.
127+
- Featured maps stories (editorial content) shown in the Featured Globe and related views.
128+
- Per-locale pages in Contentful
129+
- Content entries are translated per locale in Contentful (separate localized pages/entries).
130+
- The app requests the appropriate locale when fetching entries so users see the correct language.
131+
- Where the locale comes from
132+
- We keep the UI locale in the URL (location.query.lang) and initialize Transifex in src/app.jsx.
133+
- The same locale value is used when requesting Contentful entries.
134+
- More details
135+
- Translation docs: /_docs/dev/translation
136+
- Contentful metadata: /_docs/scientists/metadata-contentful
137+
138+
## Performance and code-splitting
139+
140+
- Pages are lazy-loaded with @loadable/component to keep the initial bundle small.
141+
- Redux reducers load on demand via reducerRegistry; avoid upfront loading of all modules.
142+
- ArcGIS CSS is linked in index.html; ArcGIS API resources are lazily used by the Scene.
143+
- Prefer reusing created layers; avoid unnecessary re-instantiation when toggling visibility/opacity.
144+
145+
## How to extend
146+
147+
Add or update layers
148+
- Follow the guide: /_docs/dev/layers/add-update
149+
- Typical steps: add/update constants/mol-layers-configs and constants/layers-urls entries; ensure the visibility/ordering path in arcgis-layer-manager is correct; test URL serialization for activeLayers.
150+
151+
Add a new scene/page
152+
- Add a route in src/router.ts (path + page key).
153+
- Create a page under src/pages/<page> and wire it in App (lazy-loaded with @loadable/component).
154+
- Compose a scene in src/containers/scenes/<new-scene> with components/scene; define sceneSettings and required managers (layer, events, zoom).
155+
- If the new state is URL-driven, add actions/selectors and update constants as needed; consider onboarding and mobile variants.
156+
157+
## Cross-links and related docs
158+
159+
- State management: /_docs/dev/architecture/state-management
160+
- Working with layers (add/update): /_docs/dev/layers/add-update
161+
- Components catalog: /_docs/dev/components
162+
- Translation: /_docs/dev/translation
163+
- Developer index and environment: /_docs/dev

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"redux": "^4.0.1",
4747
"redux-first-router": "0.0.9-rudy",
4848
"redux-thunk": "^2.3.0",
49-
"redux-tools": "github:vizzuality/redux-tools#v3.4.1",
49+
"redux-tools": "github:vizzuality/redux-tools#v3.4.1",
5050
"reselect": "^4.0.0",
5151
"sha1": "^1.1.1",
5252
"vite": "^5.3.1",

src/components/charts/arc-chart/arc-chart-component.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ function ArcChartComponent({
5757
)}
5858
</text>
5959
)}
60+
{!value && (
61+
<text
62+
x={parentWidth / 2 + (isPercentage ? 6 : 0)}
63+
y={parentHeight / 2 + 40}
64+
className={styles.label}
65+
>
66+
0
67+
{isPercentage && (
68+
<tspan className={styles.labelPercentage}>%</tspan>
69+
)}
70+
</text>
71+
)}
6072
</RadialBarChart>
6173
</ResponsiveContainer>
6274
);

src/components/country-entry-tooltip/country-entry-tooltip-component.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ function CountryEntryTooltipComponent({
214214
</span>
215215
<span className={styles.text}>
216216
{landTab
217-
? t('of additional land protection is needed (as of 2023)')
218-
: t('of additional marine protection is needed (as of 2023)')}
217+
? t('of additional land protection is needed (as of 2024)')
218+
: t('of additional marine protection is needed (as of 2024)')}
219219
</span>
220220
</div>
221221
</section>

src/components/species-table/species-table.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function SpeciesModalContainer(props) {
6363
setLoaded(false);
6464
setSpeciesList([]);
6565
const query = await layer.createQuery();
66-
query.where = `iso3 = '${state.location.payload.iso}'`;
66+
query.where = `GID_0 = '${state.location.payload.iso}'`;
6767
query.maxRecordCountFactor = '10000';
6868
const results = await layer.queryFeatures(query);
6969
const { features } = results;

src/constants/country-data-constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const COUNTRY_ATTRIBUTES = {
1212
Area_Country: 'Area_Country',
1313
SPI_ter: 'SPI_ter',
1414
Global_SPI_ter: 'Global_SPI_ter',
15-
populationSum: 'SUM',
15+
populationSum: 'pop_2020',
1616
GID_0: 'GID_0',
1717
similar_ter: 'similar_ter',
1818
prop_hm_very_high_ter: 'prop_hm_very_high_ter',

src/constants/geo-processing-services.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,13 @@ export const BIODIVERSITY_CRFS_CONFIG = {
139139

140140
export const GEOPROCESSING_SERVICES_URLS = {
141141
[BIRDS]:
142-
'https://heportal.esri.com/server/rest/services/BirdsProd_SPS_20240327/GPServer/BirdsProd_SPS',
142+
'https://heportal2.esri.com/server/rest/services/birds_prod_09_26_2025/GPServer/birds',
143143
[REPTILES]:
144-
'https://heportal.esri.com/server/rest/services/ReptilesProd_SPS_20240313/GPServer/ReptilesProd_SPS',
144+
'https://heportal2.esri.com/server/rest/services/reptiles_prod_09_26_2025/GPServer/reptiles',
145145
[MAMMALS]:
146-
'https://heportal.esri.com/server/rest/services/MammalsProd_SPS_20240327/GPServer/MammalsProd_SPS',
146+
'https://heportal2.esri.com/server/rest/services/mammals_prod_09_26_2025/GPServer/mammals',
147147
[AMPHIBIANS]:
148-
'https://heportal.esri.com/server/rest/services/AmphibiansProd_SPS_20240327/GPServer/AmphibiansProd_SPS',
148+
'https://heportal2.esri.com/server/rest/services/amphibians_prod_09_26_2025/GPServer/amphibians',
149149
[CONTEXTUAL_DATA]:
150-
'https://heportal.esri.com/server/rest/services/Contextual_Prod3_20240313/GPServer/Contextual_Prod3',
150+
'https://heportal2.esri.com/server/rest/services/contextual_info_prod_09_26_2025/GPServer/contextual_info',
151151
};

0 commit comments

Comments
 (0)