Skip to content

Commit

Permalink
Calculate Zoom based on shapefile size (#2242)
Browse files Browse the repository at this point in the history
* calculate zoom depending on shapefile size

* update map context

* Revert "update map context"

This reverts commit 08ab86f.

* Revert "Revert "update map context""

This reverts commit 5ed5976.

* fix preview bug

* code smells

* Delete met-web/package-lock.json

should not be part of this pr

* Revert "Delete met-web/package-lock.json"

This reverts commit 3059e0f.

* revert package lock

* update to most recent

* resolve conflict

* change lock file version to 1

* update package lock

* change submit text from submit form to submit

* Revert "change submit text from submit form to submit"

This reverts commit 63f7787.

* add optional dependencies to test

* update github actions

* update lock file version

* add no platform check

* update node version
  • Loading branch information
djnunez-aot authored Sep 25, 2023
1 parent 48bcfca commit c560fbf
Show file tree
Hide file tree
Showing 12 changed files with 680 additions and 524 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/met-web-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ jobs:

strategy:
matrix:
node-version: [14.x]
node-version: [16.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
Expand Down
1,023 changes: 523 additions & 500 deletions met-web/package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ type DetailsForm = yup.TypeOf<typeof schema>;

const Form = () => {
const dispatch = useAppDispatch();
const { widget, mapData, isLoadingMap, setPreviewMapOpen, setPreviewMap } = useContext(MapContext);
const { widget, mapData, isLoadingMap, setPreviewMapOpen, setPreviewMap, updateZoom } = useContext(MapContext);
const { handleWidgetDrawerOpen } = useContext(WidgetDrawerContext);
const [isCreating, setIsCreating] = useState(false);
const [uploadName, setUploadName] = useState('');
const [calculatingZoom, setCalculatingZoom] = useState(false);

const methods = useForm<DetailsForm>({
resolver: yupResolver(schema),
Expand Down Expand Up @@ -123,18 +124,19 @@ const Form = () => {
if (!valid) {
return;
}
setPreviewMapOpen(true);
if (shapefile) {
previewGeoJson = await previewShapeFile({
file: shapefile,
});
updateZoom(previewGeoJson);
}
setPreviewMap({
longitude: validatedData.longitude,
latitude: validatedData.latitude,
markerLabel: validatedData.markerLabel,
geojson: previewGeoJson ? previewGeoJson : validatedData.geojson,
});
setPreviewMapOpen(true);
};

const handleAddFile = async (files: File[]) => {
Expand All @@ -144,9 +146,12 @@ const Form = () => {
previewGeoJson = (await previewShapeFile({
file: files[0],
})) as unknown as turf.FeatureCollection<turf.Point>;
setCalculatingZoom(true);
updateZoom(previewGeoJson);
const centerPoint = turf.center(previewGeoJson as turf.FeatureCollection<turf.Point>);
methods.setValue('longitude', centerPoint.geometry.coordinates[0]);
methods.setValue('latitude', centerPoint.geometry.coordinates[1]);
setCalculatingZoom(false);
return;
}
methods.setValue('shapefile', undefined);
Expand Down Expand Up @@ -262,7 +267,9 @@ const Form = () => {
</Grid>
<Grid item xs={12} container direction="row" justifyContent={'flex-end'}>
<Grid item>
<SecondaryButton onClick={handlePreviewMap}>Preview Map</SecondaryButton>
<SecondaryButton loading={calculatingZoom} onClick={handlePreviewMap}>
Preview Map
</SecondaryButton>
</Grid>
</Grid>
<Grid
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Widget, WidgetType } from 'models/widget';
import React, { createContext, useContext, useState, useEffect } from 'react';
import { WidgetDrawerContext } from '../WidgetDrawerContext';
import { PreviewMap } from './types';
import { PreviewMap, GeoJSONInput } from './types';
import { fetchMaps } from 'services/widgetService/MapService';
import { WidgetMap } from 'models/widgetMap';
import { useAppDispatch } from 'hooks';
import { openNotification } from 'services/notificationService/notificationSlice';
import { geoJSONDecode, calculateZoomLevel } from 'components/engagement/form/EngagementWidgets/Map/utils';
import { GeoJSON } from 'geojson';

export interface MapContextProps {
widget: Widget | null;
Expand All @@ -15,6 +17,13 @@ export interface MapContextProps {
isLoadingMap: boolean;
previewMapOpen: boolean;
setPreviewMapOpen: React.Dispatch<React.SetStateAction<boolean>>;
zoomLevel: number;
setZoomLevel: React.Dispatch<React.SetStateAction<number>>;
mapWidth: number;
setMapWidth: React.Dispatch<React.SetStateAction<number>>;
mapHeight: number;
setMapHeight: React.Dispatch<React.SetStateAction<number>>;
updateZoom: (geojson: GeoJSONInput) => void;
}

export type EngagementParams = {
Expand All @@ -26,13 +35,28 @@ export const MapContext = createContext<MapContextProps>({
mapData: null,
previewMap: null,
isLoadingMap: true,
zoomLevel: 12,
updateZoom: (geojson: GeoJSONInput) => {
throw new Error('updateZoom unimplemented');
},
setZoomLevel: () => {
throw new Error('setZoomLevel unimplemented');
},
setPreviewMap: () => {
throw new Error('setPreviewMap unimplemented');
},
previewMapOpen: false,
setPreviewMapOpen: () => {
throw new Error('setPreviewMap unimplemented');
},
mapHeight: 500,
setMapHeight: () => {
throw new Error('setMapHeight unimplemented');
},
mapWidth: 500,
setMapWidth: () => {
throw new Error('setMapWidth unimplemented');
},
});

export const MapProvider = ({ children }: { children: JSX.Element | JSX.Element[] }) => {
Expand All @@ -43,6 +67,9 @@ export const MapProvider = ({ children }: { children: JSX.Element | JSX.Element[
const [previewMap, setPreviewMap] = useState<PreviewMap | null>(null);
const [previewMapOpen, setPreviewMapOpen] = useState(false);
const [isLoadingMap, setIsLoadingMap] = useState(true);
const [zoomLevel, setZoomLevel] = useState(12);
const [mapWidth, setMapWidth] = useState(500);
const [mapHeight, setMapHeight] = useState(500);

const loadMap = async () => {
if (!widget) {
Expand All @@ -52,20 +79,45 @@ export const MapProvider = ({ children }: { children: JSX.Element | JSX.Element[
setIsLoadingMap(true);
const loadedMap = await fetchMaps(widget.id);
setMapData(loadedMap[loadedMap.length - 1]);
updateZoom(loadedMap[loadedMap.length - 1]?.geojson);
setIsLoadingMap(false);
} catch (error) {
dispatch(openNotification({ severity: 'error', text: 'An error occurred while trying to load map data' }));
setIsLoadingMap(false);
}
};

const updateZoom = (geojson: GeoJSONInput) => {
const decodedGeojson = typeof geojson === 'string' ? geoJSONDecode(geojson) : geojson;

if (decodedGeojson) {
const zoom = calculateZoomLevel(mapWidth, mapHeight, decodedGeojson as GeoJSON);
setZoomLevel(zoom);
}
};

useEffect(() => {
loadMap();
}, []);

return (
<MapContext.Provider
value={{ isLoadingMap, widget, mapData, previewMap, setPreviewMap, previewMapOpen, setPreviewMapOpen }}
value={{
isLoadingMap,
widget,
mapData,
previewMap,
setPreviewMap,
previewMapOpen,
setPreviewMapOpen,
setZoomLevel,
zoomLevel,
mapHeight,
mapWidth,
setMapHeight,
setMapWidth,
updateZoom,
}}
>
{children}
</MapContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MapContext } from './MapContext';
import MetMap from 'components/map';

export const PreviewModal = () => {
const { previewMapOpen, setPreviewMapOpen, previewMap } = useContext(MapContext);
const { previewMapOpen, setPreviewMapOpen, previewMap, zoomLevel, mapHeight, mapWidth } = useContext(MapContext);

if (!previewMap) {
return null;
Expand All @@ -23,15 +23,16 @@ export const PreviewModal = () => {
<Paper sx={{ ...modalStyle, padding: '1px' }}>
<Box
sx={{
width: '500px',
height: '500px',
width: `${mapWidth}px`,
height: `${mapHeight}px`,
}}
>
<MetMap
geojson={previewMap.geojson}
longitude={previewMap.longitude}
latitude={previewMap.latitude}
markerLabel={previewMap.markerLabel}
zoom={zoomLevel}
/>
</Box>
</Paper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { GeoJSON } from 'geojson';
import * as turf from '@turf/turf';

export interface PreviewMap {
longitude: number;
latitude: number;
markerLabel?: string;
geojson?: GeoJSON;
}

export type GeoJSONInput = turf.AllGeoJSON | GeoJSON | undefined | string;
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
import { GeoJSON } from 'geojson';
import * as turf from '@turf/turf';

export const calculateZoomLevel = (mapWidth: number, mapHeight: number, geojson?: GeoJSON) => {
const ZOOM_MAX = 21;

if (geojson) {
const bbox = turf.bbox(geojson);

const latDiff = bbox[3] - bbox[1];
const lngDiff = bbox[2] - bbox[0];

const mapAspect = mapWidth / mapHeight;
const bboxAspect = lngDiff / latDiff;

let zoom;

if (mapAspect > bboxAspect) {
// limited by height, use latitude
const verticalScale = mapHeight / latDiff;
zoom = Math.log2(verticalScale) - 1; // -1 is a correction factor to prevent overshooting
} else {
// limited by width, use longitude
const horizontalScale = mapWidth / lngDiff;
zoom = Math.log2(horizontalScale) - 1; // -1 is a correction factor to prevent overshooting
}

zoom = Math.min(zoom, ZOOM_MAX);
zoom = Math.max(zoom, 0);

return zoom;
}

return 12;
};

export const geoJSONDecode = (geojson_string?: string) => {
const geojson: GeoJSON = geojson_string ? JSON.parse(geojson_string.replace(/\\/g, '')) : undefined;
Expand Down
17 changes: 15 additions & 2 deletions met-web/src/components/engagement/view/widgets/Map/ExpandModal.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import React, { useRef, useEffect, useState } from 'react';
import Modal from '@mui/material/Modal';
import { Box, Paper, Grid } from '@mui/material';
import MetMap from 'components/map';
import { WidgetMap } from 'models/widgetMap';
import { PrimaryButton } from 'components/common';
import { geoJSONDecode } from 'components/engagement/form/EngagementWidgets/Map/utils';
import { geoJSONDecode, calculateZoomLevel } from 'components/engagement/form/EngagementWidgets/Map/utils';

interface ExpandModalProps {
open: boolean;
Expand All @@ -13,6 +13,17 @@ interface ExpandModalProps {
}

export const ExpandModal = ({ open, setOpen, map }: ExpandModalProps) => {
const mapContainerRef = useRef<HTMLDivElement | null>(null);
const [mapWidth, setMapWidth] = useState(250);
const [mapHeight, setMapHeight] = useState(250);

useEffect(() => {
if (mapContainerRef.current) {
setMapWidth(mapContainerRef.current.clientWidth);
setMapHeight(mapContainerRef.current.clientHeight);
}
}, []);

if (!map) {
return null;
}
Expand Down Expand Up @@ -43,6 +54,7 @@ export const ExpandModal = ({ open, setOpen, map }: ExpandModalProps) => {
>
<Grid item>
<Box
ref={mapContainerRef}
sx={{
width: '80vw',
height: '65vh',
Expand All @@ -53,6 +65,7 @@ export const ExpandModal = ({ open, setOpen, map }: ExpandModalProps) => {
longitude={map.longitude}
latitude={map.latitude}
markerLabel={map.marker_label}
zoom={calculateZoomLevel(mapWidth, mapHeight, geoJSONDecode(map.geojson))}
/>
</Box>
</Grid>
Expand Down
14 changes: 12 additions & 2 deletions met-web/src/components/engagement/view/widgets/Map/MapWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { MetPaper, MetHeader2, MetLabel } from 'components/common';
import { Grid, Skeleton, Divider, Box, IconButton, Link, useMediaQuery, Theme } from '@mui/material';
import { Widget } from 'models/widget';
Expand All @@ -10,7 +10,7 @@ import { WidgetMap } from 'models/widgetMap';
import OpenWithIcon from '@mui/icons-material/OpenWith';
import { ExpandModal } from './ExpandModal';
import { When } from 'react-if';
import { geoJSONDecode } from 'components/engagement/form/EngagementWidgets/Map/utils';
import { geoJSONDecode, calculateZoomLevel } from 'components/engagement/form/EngagementWidgets/Map/utils';

interface MapWidgetProps {
widget: Widget;
Expand All @@ -21,7 +21,11 @@ const MapWidget = ({ widget }: MapWidgetProps) => {
const [isLoading, setIsLoading] = useState(true);
const [map, setMap] = useState<WidgetMap | null>(null);
const [open, setOpen] = useState<boolean>(false);
const mapContainerRef = useRef<HTMLDivElement | null>(null);
const isLargeScreen = useMediaQuery((theme: Theme) => theme.breakpoints.up('lg'));
const [mapWidth, setMapWidth] = useState(250);
const [mapHeight, setMapHeight] = useState(250);

const fetchMap = async () => {
try {
const map = await fetchMaps(widget.id);
Expand All @@ -41,6 +45,10 @@ const MapWidget = ({ widget }: MapWidgetProps) => {

useEffect(() => {
fetchMap();
if (mapContainerRef.current) {
setMapWidth(mapContainerRef.current.clientWidth);
setMapHeight(mapContainerRef.current.clientHeight);
}
}, [widget]);

if (isLoading) {
Expand Down Expand Up @@ -84,6 +92,7 @@ const MapWidget = ({ widget }: MapWidgetProps) => {
</Grid>
<Grid item xs={12}>
<Box
ref={mapContainerRef}
sx={{
width: '100%',
height: '370px',
Expand All @@ -94,6 +103,7 @@ const MapWidget = ({ widget }: MapWidgetProps) => {
longitude={map.longitude}
latitude={map.latitude}
markerLabel={map.marker_label}
zoom={calculateZoomLevel(mapWidth, mapHeight, geoJSONDecode(map.geojson))}
/>
</Box>
</Grid>
Expand Down
Loading

0 comments on commit c560fbf

Please sign in to comment.