as a transit agency operator,
I want to use OpenStreetMap instead of Google Maps,
to cut costs and increase flexibility
Acceptance Criteria
[x] Add support for switching between Google Maps and OSM at the .env level
[x] Use a plugin architecture so we can add other maps in the future.
Explanation
The plugin architecture would center around creating a common interface that all map provider plugins must implement. This interface would define methods for essential map operations like initializing the map, setting the view, adding markers, and handling user interactions. You'd create a base class that outlines these required methods.
To implement a specific map provider, you'd create a new class that extends this base class or implements the interface. For example, you might have an OpenStreetMapProvider class and a GoogleMapProvider class. Each of these would implement the required methods using the specific API calls and conventions of their respective mapping libraries. The rest of your application would then interact with maps through this common interface, without needing to know the details of the specific provider being used.
To switch between providers, you'd simply need to instantiate the appropriate provider class and pass it to the components that need map functionality. You could even make this configurable, allowing users to choose their preferred map provider through a settings menu. This architecture promotes modularity and makes it easier to add support for new map providers in the future without having to modify the core application logic.
Example code
// Define the base class for map providers
class MapProvider {
initMap(elementId) {
throw new Error('initMap must be implemented');
}
setView(lat, lon, zoom) {
throw new Error('setView must be implemented');
}
addMarker(lat, lon, popupText) {
throw new Error('addMarker must be implemented');
}
// Add other common methods as needed
}
// OpenStreetMap implementation
class OpenStreetMapProvider extends MapProvider {
constructor() {
super();
this.map = null;
}
initMap(elementId) {
this.map = L.map(elementId); // Using Leaflet.js for OSM
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(this.map);
}
setView(lat, lon, zoom) {
this.map.setView([lat, lon], zoom);
}
addMarker(lat, lon, popupText) {
L.marker([lat, lon]).addTo(this.map).bindPopup(popupText);
}
}
// Google Maps implementation
class GoogleMapProvider extends MapProvider {
constructor() {
super();
this.map = null;
}
initMap(elementId) {
this.map = new google.maps.Map(document.getElementById(elementId), {
center: { lat: 0, lng: 0 },
zoom: 8
});
}
setView(lat, lon, zoom) {
this.map.setCenter({ lat, lng: lon });
this.map.setZoom(zoom);
}
addMarker(lat, lon, popupText) {
new google.maps.Marker({
position: { lat, lng: lon },
map: this.map,
title: popupText
});
}
}
// Usage in a Svelte component
export default {
props: ['mapProvider'],
onMount() {
const { mapProvider } = this.props;
mapProvider.initMap('map-container');
mapProvider.setView(51.505, -0.09, 13);
mapProvider.addMarker(51.5, -0.09, 'Hello, London!');
}
};
// In your HTML
// <div id="map-container" style="height: 400px;"></div>
// In your main app or settings
const useOpenStreetMap = true;
const mapProvider = useOpenStreetMap ? new OpenStreetMapProvider() : new GoogleMapProvider();
// Pass mapProvider to your map component
User story
as a transit agency operator, I want to use OpenStreetMap instead of Google Maps, to cut costs and increase flexibility
Acceptance Criteria
.env
levelExplanation
The plugin architecture would center around creating a common interface that all map provider plugins must implement. This interface would define methods for essential map operations like initializing the map, setting the view, adding markers, and handling user interactions. You'd create a base class that outlines these required methods.
To implement a specific map provider, you'd create a new class that extends this base class or implements the interface. For example, you might have an OpenStreetMapProvider class and a GoogleMapProvider class. Each of these would implement the required methods using the specific API calls and conventions of their respective mapping libraries. The rest of your application would then interact with maps through this common interface, without needing to know the details of the specific provider being used.
To switch between providers, you'd simply need to instantiate the appropriate provider class and pass it to the components that need map functionality. You could even make this configurable, allowing users to choose their preferred map provider through a settings menu. This architecture promotes modularity and makes it easier to add support for new map providers in the future without having to modify the core application logic.
Example code