Skip to content

Commit

Permalink
split Geo tools into separate hooks & components
Browse files Browse the repository at this point in the history
  • Loading branch information
sheppard committed Jun 15, 2021
1 parent 756473f commit cdabda6
Show file tree
Hide file tree
Showing 14 changed files with 614 additions and 403 deletions.
42 changes: 42 additions & 0 deletions packages/map/src/components/GeoTools.js
@@ -0,0 +1,42 @@
import React from 'react';
import { useComponents, useInputComponents } from '@wq/react';
import { useGeoTools } from '../hooks';
import PropTypes from 'prop-types';

export default function GeoTools({ name, type }) {
const {
toggleProps,
setLocation,
setBounds,
ActiveTool,
value
} = useGeoTools(name, type),
{ View } = useComponents(),
{ Toggle } = useInputComponents();

return (
<View
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}}
>
<View style={{ marginRight: 8 }}>
<Toggle {...toggleProps} />
</View>
<ActiveTool
name={name}
value={value}
type={type}
setLocation={setLocation}
setBounds={setBounds}
/>
</View>
);
}

GeoTools.propTypes = {
name: PropTypes.string,
type: PropTypes.string
};
4 changes: 3 additions & 1 deletion packages/map/src/components/index.js
Expand Up @@ -4,6 +4,7 @@ import AutoOverlay from './AutoOverlay';
import StickyMap from './StickyMap';
import Map from './Map';
import Legend, { BasemapToggle, OverlayToggle } from './Legend';
import GeoTools from './GeoTools';

export {
AutoMap,
Expand All @@ -13,5 +14,6 @@ export {
Map,
Legend,
BasemapToggle,
OverlayToggle
OverlayToggle,
GeoTools
};
71 changes: 71 additions & 0 deletions packages/map/src/geotools/GeoCode.js
@@ -0,0 +1,71 @@
import React, { useState, useEffect } from 'react';
import { useComponents, useInputComponents, usePlugin } from '@wq/react';
import { useField, useFormikContext } from 'formik';
import PropTypes from 'prop-types';

export default function GeoCode({ name, setLocation }) {
const { IconButton } = useComponents(),
{ Input } = useInputComponents(),
[
,
{ value: address },
{ setValue: setAddress, setError: setAddressError }
] = useField(name + '_address'),
[geocodeStatus, setGeocodeStatus] = useState(null),
{ geocoder, geocoderAddress } = usePlugin('map').config,
{ values } = useFormikContext();

async function geocode() {
if (!geocoder) {
setAddressError('No geocoder plugin registered!');
return;
}
setAddressError(null);
setGeocodeStatus('Looking up location...');
try {
const result = await geocoder(address),
{ label, geometry } = result;
if (geometry) {
setLocation({
latitude: geometry.coordinates[1],
longitude: geometry.coordinates[0],
zoom: true,
save: true
});
setGeocodeStatus(label || 'Location found!');
} else {
setGeocodeStatus(label || 'Not found');
}
} catch (e) {
setAddressError(e.message || '' + e);
setGeocodeStatus(null);
}
}

useEffect(() => {
if (address === undefined && geocoderAddress) {
setDefaultAddress();
}
async function setDefaultAddress() {
setAddress(await geocoderAddress(values));
}
}, [address, values]);

return (
<>
<Input
name={name + '_address'}
label="Address"
helperText={geocodeStatus || 'Enter address or city name'}
/>
<IconButton onClick={geocode} icon="search" color="secondary" />
</>
);
}

GeoCode.toolLabel = 'Address';

GeoCode.propTypes = {
name: PropTypes.string,
setLocation: PropTypes.func
};
94 changes: 94 additions & 0 deletions packages/map/src/geotools/GeoCoords.js
@@ -0,0 +1,94 @@
import React, { useEffect } from 'react';
import { useComponents, useInputComponents } from '@wq/react';
import { useField } from 'formik';
import PropTypes from 'prop-types';

export default function GeoCoords({ name, value, type, setLocation }) {
const { IconButton } = useComponents(),
{ Input } = useInputComponents(),
longitudeName = `${name}_longitude`,
latitudeName = `${name}_latitude`,
[, { value: latitude }, { setValue: setLongitude }] = useField(
longitudeName
),
[, { value: longitude }, { setValue: setLatitude }] = useField(
latitudeName
);

useEffect(() => {
if (type !== 'geopoint') {
return;
}
if (!(value && value.type === 'Point' && value.coordinates)) {
return;
}
const [longitude, latitude] = value.coordinates;
setLongitude(longitude);
setLatitude(latitude);
}, [type, value]);

function saveLatLong() {
if (
!latitude ||
!longitude ||
Math.abs(latitude) > 90 ||
Math.abs(longitude) > 180
) {
return;
}
setLocation({
longitude,
latitude,
zoom: true,
save: true
});
}

return (
<>
<Input
name={name + '_latitude'}
label="Latitude"
type="decimal"
inputProps={{
step: 0.000001,
min: -90,
max: 90
}}
InputLabelProps={{
shrink: true
}}
style={{ marginRight: 4 }}
/>
<Input
name={name + '_longitude'}
label="Longitude"
type="decimal"
inputProps={{
step: 0.000001,
min: -180,
max: 180
}}
InputLabelProps={{
shrink: true
}}
style={{ marginLeft: 4 }}
/>
<IconButton
onClick={saveLatLong}
icon="search"
variant="filled"
color="secondary"
/>
</>
);
}

GeoCoords.toolLabel = 'Lat/Lng';

GeoCoords.propTypes = {
name: PropTypes.str,
value: PropTypes.object,
type: PropTypes.str,
setLocation: PropTypes.func
};
78 changes: 78 additions & 0 deletions packages/map/src/geotools/GeoHelp.js
@@ -0,0 +1,78 @@
import React from 'react';
import { useComponents, useMessages } from '@wq/react';
import { TYPE_MAP } from '../hooks';
import PropTypes from 'prop-types';

export default function GeoHelp({ value, type }) {
const drawType = TYPE_MAP[type] || type,
{ Typography } = useComponents(),
messageId = `GEO_${drawType.toUpperCase()}_${value ? 'EDIT' : 'NEW'}`,
{ [messageId]: messageTemplate } = useMessages(),
message = [];

if (messageTemplate) {
messageTemplate.split('{').forEach(part => {
if (message.length === 0) {
message.push(part);
return;
}
const [iconName, ...rest] = part.split('}'),
iconClass = getIconClass(iconName, drawType);
if (iconClass) {
message.push(
<span
className={iconClass}
style={{
display: 'inline-block',
width: 18,
height: 18,
verticalAlign: 'middle'
}}
/>
);
message.push(rest.join('}'));
} else {
message.push(part);
}
});
}
return (
<Typography
color="textSecondary"
style={{ flex: 1, textAlign: 'right' }}
>
{message}
</Typography>
);
}

GeoHelp.toolLabel = false;
GeoHelp.toolDefault = true;

GeoHelp.propTypes = {
value: PropTypes.object,
type: PropTypes.string
};

const SHAPES = ['point', 'line', 'polygon'],
ICONS = SHAPES.map(shape => `${shape.toUpperCase()}_ICON`);

function getIconClass(iconName, drawType) {
let shape = null;

if (iconName === 'TOOL_ICON') {
if (drawType === 'line_string') {
shape = 'line';
} else if (SHAPES.includes(drawType)) {
shape = drawType;
}
} else if (ICONS.includes(iconName)) {
shape = SHAPES[ICONS.indexOf(iconName)];
}

if (shape) {
return `mapbox-gl-draw_${shape}`;
} else {
return null;
}
}

0 comments on commit cdabda6

Please sign in to comment.