Pretty nice (IMHO) version of avatars for locations...

This commit is contained in:
Eric van der Vlist 2022-10-11 22:15:21 +02:00
parent 9ce7f33cb4
commit 9bb2f48d4a
3 changed files with 61 additions and 43 deletions

View File

@ -1,4 +1,5 @@
import React from 'react'; import { locationOutline } from 'ionicons/icons';
import React, { useId } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { geoPoint, lat2tile, lon2tile, Point } from '../../lib/geo'; import { geoPoint, lat2tile, lon2tile, Point } from '../../lib/geo';
@ -12,17 +13,15 @@ interface AvatarForLocationProps {
zoom?: number; zoom?: number;
tileProvider?: string; tileProvider?: string;
size?: number; size?: number;
name?: string;
} }
const AvatarForLocation: React.FC<AvatarForLocationProps> = ( const AvatarForLocation: React.FC<AvatarForLocationProps> = (
props: AvatarForLocationProps props: AvatarForLocationProps
) => { ) => {
const size = props.size ? props.size : 42; const size = props.size !== undefined ? props.size : 42;
const zoom = props.zoom ? Math.round(props.zoom) : 16; const zoom = props.zoom ? Math.round(props.zoom) : 16;
const tileProvider = props.tileProvider ? props.tileProvider : 'osm'; const tileProvider = props.tileProvider ? props.tileProvider : 'osm';
const location = props.location; const location = props.location;
const name = props.name ? props.name : '???';
const tilesLocation: Point = { const tilesLocation: Point = {
x: lon2tile(location.lon, zoom), x: lon2tile(location.lon, zoom),
@ -34,10 +33,10 @@ const AvatarForLocation: React.FC<AvatarForLocationProps> = (
y: 256 * (tilesLocation.y - Math.floor(tilesLocation.y)), y: 256 * (tilesLocation.y - Math.floor(tilesLocation.y)),
}; };
const eastTileNeeded = locationWithinTile.x > 256 - size; const eastTileNeeded = locationWithinTile.x > 256 - size / 2;
const westTileNeeded = locationWithinTile.x < size; const westTileNeeded = locationWithinTile.x < size / 2;
const southTileNeeded = locationWithinTile.y > 256 - size; const southTileNeeded = locationWithinTile.y > 256 - size / 2;
const northTileNeeded = locationWithinTile.y < size; const northTileNeeded = locationWithinTile.y < size / 2;
const getImage = (stepX: number, stepY: number) => ( const getImage = (stepX: number, stepY: number) => (
<image <image
@ -48,8 +47,8 @@ const AvatarForLocation: React.FC<AvatarForLocationProps> = (
)} )}
height='256' height='256'
width={256} width={256}
x={-locationWithinTile.x + stepX * 256} x={-locationWithinTile.x + size / 2 + stepX * 256}
y={-locationWithinTile.y + stepY * 256} y={-locationWithinTile.y + size / 2 + stepY * 256}
/> />
); );
@ -81,19 +80,16 @@ const AvatarForLocation: React.FC<AvatarForLocationProps> = (
console.log(`${images.length} images: ${images}`); console.log(`${images.length} images: ${images}`);
const id = useId();
return ( return (
<svg width={size} height={size}> <svg width={size} height={size}>
<defs> <defs>
<clipPath id='cut-off'> <clipPath id={`cut-off-${id}`}>
<circle cx={size / 2} cy={size / 2} r={size / 2} /> <circle cx={size / 2} cy={size / 2} r={size / 2} />
</clipPath> </clipPath>
</defs> </defs>
<g clip-path='url(#cut-off)'> <g clip-path={`url(#cut-off-${id})`}>{[images]}</g>
{[images]}
<text x='2' y={size / 2 + 5} visibility='hidden'>
{name}
</text>
</g>
</svg> </svg>
); );
}; };

View File

@ -78,7 +78,7 @@ const LocationInfo: React.FC<{}> = () => {
setNoteIndex(0); setNoteIndex(0);
const metresPerDegree = const metresPerDegree =
111111 * Math.cos((scope.center.lat * Math.PI) / 180); 111111 * Math.cos((scope.center.lat * Math.PI) / 180);
const deltaDegrees = 2000 / metresPerDegree; const deltaDegrees = 5000 / metresPerDegree;
const response = await fetch( const response = await fetch(
`https://api.openstreetmap.org/api/0.6/notes.json?bbox=${ `https://api.openstreetmap.org/api/0.6/notes.json?bbox=${
scope.center.lon - deltaDegrees scope.center.lon - deltaDegrees
@ -89,6 +89,17 @@ const LocationInfo: React.FC<{}> = () => {
); );
const data = await response.json(); const data = await response.json();
console.log(`notes: ${JSON.stringify(data)}`); console.log(`notes: ${JSON.stringify(data)}`);
data.features.sort(
(first: any, second: any) =>
roughDistance(scope.center, {
lon: first.geometry.coordinates[0],
lat: first.geometry.coordinates[1],
}) >
roughDistance(scope.center, {
lon: second.geometry.coordinates[0],
lat: second.geometry.coordinates[1],
})
);
setNotes(data); setNotes(data);
}; };
@ -121,24 +132,23 @@ const LocationInfo: React.FC<{}> = () => {
setNoteIndex(index); setNoteIndex(index);
}; };
const AvatarForFeature: React.FC<{ feature: any; as: any }> = (props: { const AvatarForFeature: React.FC<{
feature: any; feature: any;
size: number;
as: any; as: any;
}) => { }> = (props: { feature: any; size: number; as: any }) => {
const distance = roughDistance(scope.center, { const distance = roughDistance(scope.center, {
lon: props.feature.geometry.coordinates[0], lon: props.feature.geometry.coordinates[0],
lat: props.feature.geometry.coordinates[1], lat: props.feature.geometry.coordinates[1],
}); });
const distDark = (255 * distance) / 3000;
const distLight = 1 - distDark;
return ( return (
<Avatar> <Avatar size={props.size <= 50 ? 'sm' : 'lg'}>
<AvatarForLocation <AvatarForLocation
location={{ location={{
lon: props.feature.geometry.coordinates[0], lon: props.feature.geometry.coordinates[0],
lat: props.feature.geometry.coordinates[1], lat: props.feature.geometry.coordinates[1],
}} }}
name={Math.round(distance).toString()} size={props.size}
/> />
</Avatar> </Avatar>
); );
@ -205,25 +215,36 @@ const LocationInfo: React.FC<{}> = () => {
<MainContainer responsive> <MainContainer responsive>
<Sidebar position='left' id='chat-sidebar'> <Sidebar position='left' id='chat-sidebar'>
<ConversationList> <ConversationList>
{notes.features!.map((feature: any, index: number) => ( {notes
<Conversation .features!.slice(0, 5)
onClick={conversationClickHandlerFactory(index)} .map((feature: any, index: number) => (
key={feature.properties.id} <Conversation
name={i18n.locationInfo!.at!( onClick={conversationClickHandlerFactory(index)}
roughDistance(scope.center, { key={feature.properties.id}
lon: feature.geometry.coordinates[0], name={i18n.locationInfo!.at!(
lat: feature.geometry.coordinates[1], roughDistance(scope.center, {
}) lon: feature.geometry.coordinates[0],
)} lat: feature.geometry.coordinates[1],
info={feature.properties.status} })
> )}
<AvatarForFeature feature={feature} as={Avatar} /> info={feature.properties.status}
</Conversation> >
))} <AvatarForFeature
feature={feature}
size={42}
as={Avatar}
/>
</Conversation>
))}
</ConversationList> </ConversationList>
</Sidebar> </Sidebar>
<ChatContainer> <ChatContainer>
<ConversationHeader> <ConversationHeader>
<AvatarForFeature
feature={notes.features[noteIndex]}
size={68}
as={Avatar}
/>
<ConversationHeader.Content> <ConversationHeader.Content>
<span <span
style={{ style={{
@ -240,10 +261,6 @@ const LocationInfo: React.FC<{}> = () => {
)} )}
</span> </span>
</ConversationHeader.Content> </ConversationHeader.Content>
<AvatarForFeature
feature={notes.features[noteIndex]}
as={Avatar}
/>
</ConversationHeader> </ConversationHeader>
<MessageList> <MessageList>
{noteIndex < notes.features.length && {noteIndex < notes.features.length &&

View File

@ -97,3 +97,8 @@ ion-modal ion-content {
display: block; display: block;
} }
} }
.cs-conversation-header__avatar {
height: auto !important;
width:auto !important;
}