Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add and use MapLibre in React if running Terrastories in offline mode #943

Merged
merged 7 commits into from
Oct 20, 2023

Conversation

rudokemper
Copy link
Member

@rudokemper rudokemper commented Sep 22, 2023

Addresses one chunk of the work described in #882.

This PR implements MapLibre for the React front end map at the /home/ route, if in offline mode.

  • Added maplibre-gl to package.json.
  • In Map.jsx, I created a new prop mapGL which is set to Maplibre if useLocalMapServer is true (with fallback Mapbox), and applied it across the code.
  • Imported the Maplibre CSS files in addition to the Mapbox ones.
  • Added the MapLibre logo on the bottom right for proper attribution.
  • Changed the prop mapboxStyle to be named mapStyle instead.

Screenshot from 2023-09-22 15-02-33

The next chunk of work will be on applying MapLibre in Rails if in offline mode, but that be a separate (follow-on) PR.

@rudokemper
Copy link
Member Author

Follow up PR to take care of the Rails part: #944

@rudokemper rudokemper linked an issue Sep 23, 2023 that may be closed by this pull request
6 tasks
@rudokemper rudokemper marked this pull request as draft September 27, 2023 21:32
@rudokemper
Copy link
Member Author

rudokemper commented Sep 27, 2023

As of today, this PR is pulling in both mapbox and maplibre which will unnecessarily bloat our assets. We will need to find a way to dynamically determine pulling only one of the two libraries as per the Rails environment, env vars, or a different means.

One possibility might be to incorporate these mapbox/maplibre React components: https://visgl.github.io/react-map-gl/docs

@rudokemper
Copy link
Member Author

I implemented dynamic importing to ensure only one library is being pulled on the React side.

I tried to include the CSS imports as well, but that resulted in things like the map controls or minimap (for Mapbox) not rendering at all.

In these screenshots you can see that the non-utilized map library were not pulled by Webpack:

Mapbox:
image

Maplibre:
image

(Also, small but meaningful thing: I had the Minimap use the same style as the map itself.)

@rudokemper rudokemper marked this pull request as ready for review September 28, 2023 03:09
@rudokemper
Copy link
Member Author

Would be great to get this one in the new offline-field-kit build :)

Copy link
Contributor

@lauramosher lauramosher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good overall. I have a couple of questions/clarifications on the import placement and usage of the map library switching.

Comment on lines 37 to 50
if (this.props.useLocalMapServer) {
import('!maplibre-gl').then(module => {
this.initializeMap(module.default, true);
});
} else {
Promise.all([
import('!mapbox-gl'),
import('../vendor/mapboxgl-control-minimap.js')
]).then(([mapboxGLModule, minimapModule]) => {
this.Minimap = minimapModule.default;
this.initializeMap(mapboxGLModule.default, false);
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we could move the conditional imports into the constructor rather than on mount. We likely want the libraries imported and ready to go before the component has mounted and our rendering starts. I have an unsubstantiated concern that we may inadvertently re-import these libraries if React decides it needs to remount.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I couldn't quite figure this out -- to the best of my knowledge and research, dynamic imports are inherently asynchronous, whereas constructors are not and can't handle Promises. Do you have any suggestions for how to handle this via the constructor?

However, I did implement a check to see if the modules have already been imported by setting that in the state. If they haven't, we import and store the modules, and if not, we skip and call initializeMap with the already imported modules.

rails/app/javascript/components/Map.jsx Outdated Show resolved Hide resolved
Copy link
Contributor

@lauramosher lauramosher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@rudokemper rudokemper merged commit 5ee441a into master Oct 20, 2023
@rudokemper rudokemper deleted the rk/maplibre-for-offline branch October 20, 2023 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Map] Use MapLibre for offline environment
2 participants