First steps at supporting other map tile servers (#7)
This commit is contained in:
parent
32f147bdb6
commit
ac67a063c2
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
|
||||
import { IonButton, IonIcon } from '@ionic/react';
|
||||
import { layersOutline } from 'ionicons/icons';
|
||||
const TileServerChooserButton: React.FC<{}> = () => {
|
||||
return (
|
||||
<IonButton id='open-TileServerChooser'>
|
||||
<IonIcon slot='icon-only' icon={layersOutline} />
|
||||
</IonButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default TileServerChooserButton;
|
|
@ -0,0 +1,52 @@
|
|||
import {
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonModal,
|
||||
IonRadio,
|
||||
IonRadioGroup,
|
||||
IonTitle,
|
||||
} from '@ionic/react';
|
||||
import React from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { mapActions, MapState } from '../../store/map';
|
||||
import { tileProviders } from './tile';
|
||||
|
||||
const TileServerChooserDialog: React.FC<{}> = () => {
|
||||
|
||||
const tileProvider = useSelector(
|
||||
(state: { map: MapState }) => state.map.scope.tileProvider
|
||||
) as keyof typeof tileProviders;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const changeHandler = (event: any) => {
|
||||
dispatch(mapActions.setTileProvider(event.detail.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<IonModal trigger='open-TileServerChooser'>
|
||||
<IonHeader>
|
||||
<IonTitle>Select your map</IonTitle>
|
||||
</IonHeader>
|
||||
<IonContent>
|
||||
<IonList>
|
||||
<IonRadioGroup value={tileProvider} onIonChange={changeHandler}>
|
||||
{Object.keys(tileProviders).map((provider) => {
|
||||
return (
|
||||
<IonItem key={provider}>
|
||||
<IonLabel> {tileProviders[provider].name}</IonLabel>
|
||||
<IonRadio slot='start' value={provider} />
|
||||
</IonItem>
|
||||
);
|
||||
})}
|
||||
</IonRadioGroup>
|
||||
</IonList>
|
||||
</IonContent>
|
||||
</IonModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default TileServerChooserDialog;
|
|
@ -23,6 +23,8 @@ import GpxImport from './gpx-import';
|
|||
import Gpxes from './gpxes';
|
||||
import GpxRecord from './gpx-record';
|
||||
import { initDb } from '../../db';
|
||||
import TileServerChooserButton from './TileServerChooserButton';
|
||||
import TileServerChooserDialog from './TileServerChooserDialog';
|
||||
|
||||
const Map: react.FC<{}> = (props: {}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -48,6 +50,7 @@ const Map: react.FC<{}> = (props: {}) => {
|
|||
<>
|
||||
<IonContent fullscreen={true}>
|
||||
<IonApp>
|
||||
<TileServerChooserDialog />
|
||||
<Slippy>
|
||||
<Whiteboard>
|
||||
<CurrentLocation />
|
||||
|
@ -62,6 +65,7 @@ const Map: react.FC<{}> = (props: {}) => {
|
|||
<IonHeader className='ion-no-border' translucent={true}>
|
||||
<IonToolbar>
|
||||
<IonButtons slot='end'>
|
||||
<TileServerChooserButton />
|
||||
<GpxRecord />
|
||||
<GpxImport />
|
||||
</IonButtons>
|
||||
|
|
|
@ -1,7 +1,103 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { MapState } from '../../store/map';
|
||||
|
||||
const tileProvider = (zoom: number, x: number, y: number) =>
|
||||
'https://tile.openstreetmap.org/' + zoom + '/' + x + '/' + y + '.png';
|
||||
export interface TileProvider {
|
||||
name: string;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
getTileUrl: { (zoom: number, x: number, y: number): string };
|
||||
}
|
||||
|
||||
const getRandomItem = (items: any[]) => {
|
||||
const idx = Math.floor(Math.random() * items.length);
|
||||
return items[idx];
|
||||
};
|
||||
|
||||
const abc = ['a', 'b', 'c'];
|
||||
|
||||
export const tileProviders: any = {
|
||||
osm: {
|
||||
name: 'Open Street Map',
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
getTileUrl: (zoom: number, x: number, y: number) =>
|
||||
'https://tile.openstreetmap.org/' + zoom + '/' + x + '/' + y + '.png',
|
||||
},
|
||||
osmfr: {
|
||||
name: 'Open Street Map France',
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
getTileUrl: (zoom: number, x: number, y: number) =>
|
||||
'https://' +
|
||||
getRandomItem(abc) +
|
||||
'.tile.openstreetmap.fr/osmfr/' +
|
||||
zoom +
|
||||
'/' +
|
||||
x +
|
||||
'/' +
|
||||
y +
|
||||
'.png',
|
||||
},
|
||||
otm: {
|
||||
name: 'Open Topo Map',
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
getTileUrl: (zoom: number, x: number, y: number) =>
|
||||
'https://' +
|
||||
getRandomItem(abc) +
|
||||
'.tile.opentopomap.org/' +
|
||||
zoom +
|
||||
'/' +
|
||||
x +
|
||||
'/' +
|
||||
y +
|
||||
'.png',
|
||||
},
|
||||
cyclosm: {
|
||||
name: 'CyclOSM',
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
getTileUrl: (zoom: number, x: number, y: number) =>
|
||||
'https://' +
|
||||
getRandomItem(abc) +
|
||||
'.tile-cyclosm.openstreetmap.fr/cyclosm/' +
|
||||
zoom +
|
||||
'/' +
|
||||
x +
|
||||
'/' +
|
||||
y +
|
||||
'.png',
|
||||
},
|
||||
// cyclosmlite: {
|
||||
// name: 'CyclOSM lite',
|
||||
// minZoom: 0,
|
||||
// maxZoom: 19,
|
||||
// getTileUrl: (zoom: number, x: number, y: number) =>
|
||||
// 'https://' +
|
||||
// getRandomItem(abc) +
|
||||
// '.tile-cyclosm.openstreetmap.fr/cyclosm-lite/' +
|
||||
// zoom +
|
||||
// '/' +
|
||||
// x +
|
||||
// '/' +
|
||||
// y +
|
||||
// '.png',
|
||||
// },
|
||||
// esrisat: {
|
||||
// name: 'ESRI Satellite',
|
||||
// minZoom: 0,
|
||||
// maxZoom: 19,
|
||||
// getTileUrl: (zoom: number, x: number, y: number) =>
|
||||
// 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/' +
|
||||
// zoom +
|
||||
// '/' +
|
||||
// x +
|
||||
// '/' +
|
||||
// y +
|
||||
// '.jpg',
|
||||
// },
|
||||
};
|
||||
|
||||
const Tile: React.FC<{
|
||||
ix: number;
|
||||
|
@ -16,9 +112,13 @@ const Tile: React.FC<{
|
|||
y: number;
|
||||
zoom: number;
|
||||
}) => {
|
||||
const tileProvider = useSelector(
|
||||
(state: { map: MapState }) => state.map.scope.tileProvider
|
||||
) as keyof typeof tileProviders;
|
||||
|
||||
return (
|
||||
<img
|
||||
src={tileProvider(props.zoom, props.x, props.y)}
|
||||
src={tileProviders[tileProvider].getTileUrl(props.zoom, props.x, props.y)}
|
||||
className='tile'
|
||||
alt=''
|
||||
/>
|
||||
|
|
|
@ -18,10 +18,12 @@ export const zoom0 = 18;
|
|||
export interface MapScope {
|
||||
center: geoPoint;
|
||||
zoom: number;
|
||||
tileProvider: string;
|
||||
}
|
||||
var initialMapScope: MapScope = {
|
||||
center: { lat: -37.8403508, lon: 77.5539501 },
|
||||
zoom: 15,
|
||||
tileProvider: 'osm',
|
||||
};
|
||||
|
||||
// Derived properties
|
||||
|
@ -197,6 +199,9 @@ const mapSlice = createSlice({
|
|||
name: 'map',
|
||||
initialState: initialMapState,
|
||||
reducers: {
|
||||
setTileProvider: (state, action) => {
|
||||
state.scope.tileProvider = action.payload;
|
||||
},
|
||||
resize: (state) => {
|
||||
evaluateStateFromScope(state);
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue