Skip to content

Commit

Permalink
Map: add 3D terrain control when user tilts (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
zbycz authored Sep 18, 2023
1 parent 0342b29 commit c80c173
Show file tree
Hide file tree
Showing 10 changed files with 351 additions and 132 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"js-cookie": "^2.2.1",
"jss": "^10.6.0",
"lodash": "^4.17.21",
"maplibre-gl": "^1.14.0",
"maplibre-gl": "^3.3.1",
"next": "^13.4.3",
"next-cookies": "^2.0.3",
"next-pwa": "^5.2.21",
Expand Down
4 changes: 2 additions & 2 deletions src/components/FeaturePanel/Coordinates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ const LinkItem = ({ href, label }) => (

// Our map uses 512 tiles, so our zoom is "one less"
// https://wiki.openstreetmap.org/wiki/Zoom_levels#Mapbox_GL
const MAPBOXGL_ZOOM_DIFFERENCE = 1;
const MAPLIBREGL_ZOOM_DIFFERENCE = 1;

const useGetItems = ([lon, lat]: PositionBoth) => {
const { feature } = useFeatureContext();
const { view } = useMapStateContext();
const [ourZoom] = view;

const zoom = parseFloat(ourZoom) + MAPBOXGL_ZOOM_DIFFERENCE;
const zoom = parseFloat(ourZoom) + MAPLIBREGL_ZOOM_DIFFERENCE;
const zoomInt = Math.round(zoom);
const osmQuery = feature?.osmMeta?.id
? `${feature.osmMeta.type}/${feature.osmMeta.id}`
Expand Down
6 changes: 4 additions & 2 deletions src/components/Map/BrowserMap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import 'maplibre-gl/dist/maplibre-gl.css';
import maplibregl from 'maplibre-gl';
import { useAddMapEvent, useMapEffect, useMobileMode } from '../helpers';
import { useMapStateContext } from '../utils/MapStateContext';
import { useFeatureContext } from '../utils/FeatureContext';
Expand All @@ -10,6 +9,8 @@ import { useUpdateViewOnMove } from './behaviour/useUpdateViewOnMove';
import { useUpdateStyle } from './behaviour/useUpdateStyle';
import { useInitMap } from './behaviour/useInitMap';
import { Translation } from '../../services/intl';
import { useToggleTerrainControl } from './behaviour/useToggleTerrainControl';
import { isWebglSupported } from './helpers';

const useOnMapLoaded = useAddMapEvent((map, onMapLoaded) => ({
eventType: 'load',
Expand All @@ -32,7 +33,7 @@ const NotSupportedMessage = () => (
// TODO https://cdn.klokantech.com/openmaptiles-language/v1.0/openmaptiles-language.js + use localized name in FeaturePanel

const BrowserMap = ({ onMapLoaded }) => {
if (!maplibregl.supported()) {
if (!isWebglSupported()) {
onMapLoaded();
return <NotSupportedMessage />;
}
Expand All @@ -46,6 +47,7 @@ const BrowserMap = ({ onMapLoaded }) => {
const { viewForMap, setViewFromMap, setBbox, activeLayers } =
useMapStateContext();
useUpdateViewOnMove(map, setViewFromMap, setBbox);
useToggleTerrainControl(map);
useUpdateMap(map, viewForMap);
useUpdateStyle(map, activeLayers);

Expand Down
38 changes: 38 additions & 0 deletions src/components/Map/behaviour/useToggleTerrainControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable no-underscore-dangle */
import maplibregl from 'maplibre-gl';
import { useAddMapEvent } from '../../helpers';

class OsmappTerrainControl extends maplibregl.TerrainControl {
_toggleTerrain = () => {
if (this._map.getTerrain()) {
this._map.setTerrain(null);
this._map.setMaxPitch(60);
} else {
this._map.setTerrain(this.options);
this._map.setMaxPitch(85);
}
this._updateTerrainIcon();
};
}

const terrainControl = new OsmappTerrainControl({
source: 'terrain',
exaggeration: 1,
});

let added = false;

export const useToggleTerrainControl = useAddMapEvent((map) => ({
eventType: 'move',
eventHandler: () => {
if (map.getPitch() > 0) {
if (!added) {
map.addControl(terrainControl);
added = true;
}
} else if (added) {
map.removeControl(terrainControl);
added = false;
}
},
}));
6 changes: 6 additions & 0 deletions src/components/Map/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export const OSMAPP_SOURCES = {
'terrain-rgb': {
url: `https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=${apiKey}`,
type: 'raster-dem' as const,
tileSize: 256,
},
terrain: {
url: `https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=${apiKey}`,
type: 'raster-dem' as const,
tileSize: 256,
},
outdoor: {
url: `https://api.maptiler.com/tiles/outdoor/tiles.json?key=${apiKey}`,
Expand Down
23 changes: 23 additions & 0 deletions src/components/Map/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,26 @@ export const convertOsmIdToMapId = (apiId: OsmApiId) => {
const osmToMapType = { node: 0, way: 1, relation: 4 };
return parseInt(`${apiId.id}${osmToMapType[apiId.type]}`, 10);
};

// maplibregl.supported() no longer exists
// copied from https://maplibre.org/maplibre-gl-js/docs/examples/check-for-support/
export const isWebglSupported = () => {
if (window.WebGLRenderingContext) {
const canvas = document.createElement('canvas');
try {
// Note that { failIfMajorPerformanceCaveat: true } can be passed as a second argument
// to canvas.getContext(), causing the check to fail if hardware rendering is not available. See
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
// for more details.
const context = canvas.getContext('webgl2') || canvas.getContext('webgl');
if (context && typeof context.getParameter === 'function') {
return true;
}
} catch (e) {
// WebGL is supported, but disabled
}
return false;
}
// WebGL not supported
return false;
};
4 changes: 2 additions & 2 deletions src/components/Map/styles/rasterStyle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import maplibregl from 'maplibre-gl';
import type { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
import { GLYPHS, OSMAPP_SOURCES, OSMAPP_SPRITE } from '../consts';

const getSource = (url) => {
Expand All @@ -21,7 +21,7 @@ const getSource = (url) => {
};
};

export const rasterStyle = (id, url): maplibregl.Style => {
export const rasterStyle = (id, url): StyleSpecification => {
const source = getSource(url);
return {
version: 8,
Expand Down
8 changes: 4 additions & 4 deletions src/helpers/GlobalStyle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,25 @@ export const GlobalStyle = createGlobalStyle`
margin-top: 0;
}
.mapboxgl-ctrl-group {
.maplibregl-ctrl-group {
background: ${({ theme }) => theme.palette.background.default} !important;
.mapboxgl-ctrl-icon {
.maplibregl-ctrl-icon {
filter: ${({ theme }) => theme.palette.invertFilter};
}
button+button {
border-top: 1px solid ${({ theme }) => theme.palette.divider};
}
}
.mapboxgl-ctrl-top-right {
.maplibregl-ctrl-top-right {
top: ${83 + 72}px !important;
@media ${isDesktop} {
top: 83px !important;
}
}
.mapboxgl-canvas:not(:focus) {
.maplibregl-canvas:not(:focus) {
outline: 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/__tests__/osmApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jest.mock('next/config', () => () => ({
describe('fetchFeature', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.spyOn(tagging, 'fetchSchemaTranslations').mockResolvedValue(true);
jest.spyOn(tagging, 'fetchSchemaTranslations').mockResolvedValue(undefined);
jest
.spyOn(idTaggingScheme, 'getSchemaForFeature')
.mockReturnValue(undefined); // this is covered in idTaggingScheme.test.ts
Expand Down
Loading

1 comment on commit c80c173

@vercel
Copy link

@vercel vercel bot commented on c80c173 Sep 18, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

osmapp – ./

osmapp-git-master-zbycz.vercel.app
osmapp-zbycz.vercel.app
osmapp.org
osmapp.vercel.app

Please sign in to comment.