Skip to content

Commit

Permalink
MemberFeatures: show all members for some relations (#228)
Browse files Browse the repository at this point in the history
  • Loading branch information
zbycz authored Jan 19, 2024
1 parent f782034 commit fa4d465
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/components/FeaturePanel/FeaturePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { getLabel } from '../../helpers/featureLabel';
import { ImageSection } from './ImageSection/ImageSection';
import { PublicTransport } from './PublicTransport/PublicTransport';
import { Properties } from './Properties/Properties';
import { MemberFeatures } from './MemberFeatures';

export const FeaturePanel = () => {
const { feature } = useFeatureContext();
Expand Down Expand Up @@ -56,6 +57,7 @@ export const FeaturePanel = () => {
key={getUrlOsmId(osmMeta) + (deleted && 'del')}
/>

<MemberFeatures />
{advanced && <Members />}

<PublicTransport tags={tags} />
Expand Down
9 changes: 1 addition & 8 deletions src/components/FeaturePanel/ImageSection/PoiDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from 'react';
import styled from 'styled-components';
import { hasName } from '../../../helpers/featureLabel';
import { getSubclass, hasName } from '../../../helpers/featureLabel';
import { useFeatureContext } from '../../utils/FeatureContext';
import { t } from '../../../services/intl';
import Maki from '../../utils/Maki';
import { Feature } from '../../../services/types';

const PoiType = styled.div`
color: #fff;
Expand All @@ -23,12 +22,6 @@ const PoiType = styled.div`
}
`;

const getSubclass = ({ layer, osmMeta, properties, schema }: Feature) =>
schema?.label ||
properties.subclass?.replace(/_/g, ' ') ||
(layer && layer.id) || // layer.id specified only when maplibre-gl skeleton displayed
osmMeta.type;

export const PoiDescription = () => {
const { feature } = useFeatureContext();
const { properties } = feature;
Expand Down
73 changes: 73 additions & 0 deletions src/components/FeaturePanel/MemberFeatures.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { Box, Typography } from '@material-ui/core';
import Router from 'next/router';
import { getOsmappLink, getUrlOsmId } from '../../services/helpers';
import { useFeatureContext } from '../utils/FeatureContext';
import { Feature } from '../../services/types';
import { getLabel } from '../../helpers/featureLabel';
import { useUserThemeContext } from '../../helpers/theme';
import { useMobileMode } from '../helpers';
import Maki from '../utils/Maki';

const Item = ({ feature }: { feature: Feature }) => {
const { currentTheme } = useUserThemeContext();
const mobileMode = useMobileMode();
const { setPreview } = useFeatureContext();
const { properties, tags, osmMeta } = feature;
const handleClick = (e) => {
e.preventDefault();
setPreview(null);
Router.push(`/${getUrlOsmId(osmMeta)}${window.location.hash}`);
};
const handleHover = () =>
feature.center && setPreview({ ...feature, noPreviewButton: true });

return (
<li>
<a
href={`/${getUrlOsmId(osmMeta)}`}
onClick={handleClick}
onMouseEnter={mobileMode ? undefined : handleHover}
onMouseLeave={() => setPreview(null)}
>
<Maki
ico={properties.class}
title={`${Object.keys(tags).length} keys / ${
properties.class ?? ''
} / ${properties.subclass}`}
invert={currentTheme === 'dark'}
/>
{getLabel(feature)}
</a>
</li>
);
};

export const MemberFeatures = () => {
const {
feature: { memberFeatures, tags },
} = useFeatureContext();

if (!memberFeatures?.length) {
return null;
}

const heading =
tags.climbing === 'crag'
? 'Routes'
: tags.climbing === 'area'
? 'Crags'
: 'Subitems';
return (
<Box mt={4}>
<Typography variant="overline" display="block" color="textSecondary">
{heading}
</Typography>
<ul>
{memberFeatures.map((item) => (
<Item key={getOsmappLink(item)} feature={item} />
))}
</ul>
</Box>
);
};
2 changes: 1 addition & 1 deletion src/components/Map/behaviour/useFeatureMarker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const useFeatureMarker = (map) => {
useUpdateFeatureMarker(map, feature);
useUpdatePreviewMarker(map, preview);

// hide the icon when tiles are fetched TODO sometimes broken (zoom problem)
// hide the icon when tiles are fetched TODO sometimes broken (zoom problem) (also maybe causes webgl blackout)
useEffect(() => {
if (map) {
const handle = setInterval(() => {
Expand Down
7 changes: 5 additions & 2 deletions src/helpers/featureLabel.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Feature } from '../services/types';
import { roundedToDeg } from '../utils';

const getSubclass = ({ properties, osmMeta, schema }: Feature) =>
schema?.label || properties.subclass?.replace(/_/g, ' ') || osmMeta.type;
export const getSubclass = ({ layer, osmMeta, properties, schema }: Feature) =>
schema?.label ||
properties.subclass?.replace(/_/g, ' ') ||
(layer && layer.id) || // layer.id specified only when maplibre-gl skeleton displayed
osmMeta.type;

const getRef = (feature: Feature) =>
feature.tags.ref ? `${getSubclass(feature)} ${feature.tags.ref}` : '';
Expand Down
57 changes: 45 additions & 12 deletions src/services/osmApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getApiId, getShortId, getUrlOsmId, prod } from './helpers';
import { getApiId, getShortId, getUrlOsmId, OsmApiId, prod } from './helpers';
import { FetchError, fetchJson } from './fetch';
import { Feature, Position } from './types';
import { removeFetchCache } from './fetchCache';
Expand Down Expand Up @@ -97,31 +97,64 @@ const osmToFeature = (element): Feature => {
};
};

async function fetchFeatureWithCenter(apiId: OsmApiId) {
const [element, center] = await Promise.all([
getOsmPromise(apiId),
getCenterPromise(apiId),
fetchSchemaTranslations(), // TODO this should be mocked in test??? could be moved to setIntl or something
]);

const feature = osmToFeature(element);
if (center) {
feature.center = center;
}

return addSchemaToFeature(feature);
}

const shouldFetchMembers = (feature: Feature) =>
feature.osmMeta.type === 'relation' &&
(feature.tags.climbing === 'crag' || feature.tags.climbing === 'area');

// TODO we can probably fetch full.json for all relations eg https://api.openstreetmap.org/api/0.6/relation/14334600/full.json - lets measure how long it takes for different sizes
export const addMemberFeatures = async (feature: Feature) => {
if (!shouldFetchMembers(feature)) {
return feature;
}

const start = performance.now();

const apiIds = feature.members.map(({ type, ref }) => ({ type, id: ref }));
const promises = apiIds.map((apiId) => fetchFeatureWithCenter(apiId)); // TODO optimize n+1 center-requests or fetch full
const memberFeatures = await Promise.all(promises);

const duration = Math.round(performance.now() - start);
console.log(`addMemberFeaturesToCrag took ${duration} ms`); // eslint-disable-line no-console

return {
...feature,
memberFeatures,
};
};

export const fetchFeature = async (shortId): Promise<Feature> => {
if (!shortId) {
return null;
}

try {
const apiId = getApiId(shortId);
const [element, center] = await Promise.all([
getOsmPromise(apiId),
getCenterPromise(apiId),
fetchSchemaTranslations(), // TODO this should be mocked in test??? could be moved to setIntl or something
]);

const feature = osmToFeature(element);
if (center) {
feature.center = center;
}
const withCenter = await fetchFeatureWithCenter(apiId);
const withMembers = await addMemberFeatures(withCenter);

return addSchemaToFeature(feature);
return withMembers;
} catch (e) {
console.error(`fetchFeature(${shortId}):`, e); // eslint-disable-line no-console

const error = (
e instanceof FetchError ? e.code : 'unknown'
) as Feature['error'];

return {
type: 'Feature',
skeleton: true,
Expand Down
1 change: 1 addition & 0 deletions src/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface Feature {
};
tags: FeatureTags;
members?: RelationMember[];
memberFeatures?: Feature[];
properties: {
class: string;
subclass: string;
Expand Down

1 comment on commit fa4d465

@vercel
Copy link

@vercel vercel bot commented on fa4d465 Jan 19, 2024

Choose a reason for hiding this comment

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

Please sign in to comment.