Skip to content

Commit

Permalink
feat : 지도 위치 검색 레이아웃 개발 완료 (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmjjaa authored Dec 1, 2024
1 parent 9a971d4 commit 5624c71
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/components/MapPage/Maplibre/Maplibre.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Maplibre } from './Maplibre';
const meta: Meta<typeof Maplibre> = {
component: Maplibre,
title: 'components/Maplibre',
tags: ['autodocs'],
parameters: {
docs: {
description: {
component: '검색 기능이 없는 기본 Maplibre 지도입니다.'
}
}
}
};
export default meta;

type Story = StoryObj<typeof Maplibre>;

export const Default: Story = {};
31 changes: 31 additions & 0 deletions src/components/MapPage/Maplibre/MaplibreWithSearch.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Meta, StoryObj } from '@storybook/react';
import { MaplibreWithSearch } from './MaplibreWithSearch';

const meta: Meta<typeof MaplibreWithSearch> = {
component: MaplibreWithSearch,
title: 'components/Maplibre/WithSearch',
tags: ['autodocs'],
parameters: {
docs: {
description: {
component: '검색 기능이 포함된 Maplibre 지도입니다.'
}
}
}
};
export default meta;

type Story = StoryObj<typeof MaplibreWithSearch>;

export const WithSearch: Story = {
args: {
searchText: '서울'
},
parameters: {
docs: {
description: {
story: '검색어가 포함된 지도입니다.'
}
}
}
};
119 changes: 119 additions & 0 deletions src/components/MapPage/Maplibre/MaplibreWithSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useState, useEffect } from 'react';
import Map, { Marker } from 'react-map-gl/maplibre';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import mapStyle from './map_style.json';
import { StyleSpecification } from 'maplibre-gl';
import { IoIosSearch } from 'react-icons/io';

export const MaplibreWithSearch = () => {
const [searchText, setSearchText] = useState('');
const [currentLocation, setCurrentLocation] = useState<{
longitude: number;
latitude: number;
} | null>(null);
const [direction, setDirection] = useState<number | null>(null);
const [viewState, setViewState] = useState({
longitude: 127.0,
latitude: 37.5,
zoom: 11
});

useEffect(() => {
if ('geolocation' in navigator) {
const geoWatchId = navigator.geolocation.watchPosition(
(position) => {
const newLocation = {
longitude: position.coords.longitude,
latitude: position.coords.latitude
};
setCurrentLocation(newLocation);
setViewState((prev) => ({
...prev,
...newLocation,
zoom: 15
}));
},
(error) => console.error('위치 가져오기 실패:', error),
{ enableHighAccuracy: true, maximumAge: 10000, timeout: 5000 }
);

return () => navigator.geolocation.clearWatch(geoWatchId);
}

const handleOrientation = (event: DeviceOrientationEvent) =>
setDirection(event.alpha || 0);
window.addEventListener('deviceorientation', handleOrientation);

return () =>
window.removeEventListener('deviceorientation', handleOrientation);
}, []);

const handleSearch = async () => {
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchText)}`
);
const data = await response.json();

if (data.length > 0) {
setViewState({
longitude: parseFloat(data[0].lon),
latitude: parseFloat(data[0].lat),
zoom: 16
});
} else {
alert('검색 결과가 없습니다.');
}
} catch (error) {
console.error('검색 중 에러:', error);
}
};

return (
<div className="relative w-full h-[500px]">
<div className="absolute top-2 left-1/2 transform -translate-x-1/2 z-10 bg-white rounded-lg shadow-md p-2 flex items-center space-x-2">
<input
type="text"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
placeholder="원하는 위치를 검색해보세요!"
className="w-56 p-2 rounded-md outline-none"
/>
<button onClick={handleSearch} className="px-4 py-2">
<IoIosSearch />
</button>
</div>

<Map
{...viewState}
onMove={(evt) => setViewState(evt.viewState)}
initialViewState={viewState}
style={{ width: '100%', height: '100%' }}
mapStyle={mapStyle as StyleSpecification}
mapLib={maplibregl}
minZoom={6}
maxZoom={18}
>
{currentLocation && (
<Marker
longitude={currentLocation.longitude}
latitude={currentLocation.latitude}
anchor="bottom"
rotation={direction || 0}
>
<img
src="https://www.svgrepo.com/show/372536/map-marker.svg"
alt="marker"
style={{
width: '30px',
height: '30px',
transform: 'translate(-50%, -100%)'
}}
/>
</Marker>
)}
</Map>
</div>
);
};

0 comments on commit 5624c71

Please sign in to comment.