From c6d2eb138d3216ce36176e15fc3463c6278c279f Mon Sep 17 00:00:00 2001 From: jaja Date: Sat, 30 Nov 2024 19:38:52 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=EC=A7=80=EB=8F=84=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EA=B2=80=EC=83=89=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B0=9C=EB=B0=9C=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MapPage/Maplibre/Maplibre.stories.tsx | 19 +++ .../Maplibre/MaplibreWithSearch.stories.tsx | 31 +++++ .../MapPage/Maplibre/MaplibreWithSearch.tsx | 119 ++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 src/components/MapPage/Maplibre/Maplibre.stories.tsx create mode 100644 src/components/MapPage/Maplibre/MaplibreWithSearch.stories.tsx create mode 100644 src/components/MapPage/Maplibre/MaplibreWithSearch.tsx diff --git a/src/components/MapPage/Maplibre/Maplibre.stories.tsx b/src/components/MapPage/Maplibre/Maplibre.stories.tsx new file mode 100644 index 00000000..e7246149 --- /dev/null +++ b/src/components/MapPage/Maplibre/Maplibre.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Maplibre } from './Maplibre'; +const meta: Meta = { + component: Maplibre, + title: 'components/Maplibre', + tags: ['autodocs'], + parameters: { + docs: { + description: { + component: '검색 기능이 없는 기본 Maplibre 지도입니다.' + } + } + } +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/src/components/MapPage/Maplibre/MaplibreWithSearch.stories.tsx b/src/components/MapPage/Maplibre/MaplibreWithSearch.stories.tsx new file mode 100644 index 00000000..7d5e4ae5 --- /dev/null +++ b/src/components/MapPage/Maplibre/MaplibreWithSearch.stories.tsx @@ -0,0 +1,31 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { MaplibreWithSearch } from './MaplibreWithSearch'; + +const meta: Meta = { + component: MaplibreWithSearch, + title: 'components/Maplibre/WithSearch', + tags: ['autodocs'], + parameters: { + docs: { + description: { + component: '검색 기능이 포함된 Maplibre 지도입니다.' + } + } + } +}; +export default meta; + +type Story = StoryObj; + +export const WithSearch: Story = { + args: { + searchText: '서울' + }, + parameters: { + docs: { + description: { + story: '검색어가 포함된 지도입니다.' + } + } + } +}; diff --git a/src/components/MapPage/Maplibre/MaplibreWithSearch.tsx b/src/components/MapPage/Maplibre/MaplibreWithSearch.tsx new file mode 100644 index 00000000..9c3b01e3 --- /dev/null +++ b/src/components/MapPage/Maplibre/MaplibreWithSearch.tsx @@ -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(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 ( +
+
+ setSearchText(e.target.value)} + placeholder="원하는 위치를 검색해보세요!" + className="w-56 p-2 rounded-md outline-none" + /> + +
+ + setViewState(evt.viewState)} + initialViewState={viewState} + style={{ width: '100%', height: '100%' }} + mapStyle={mapStyle as StyleSpecification} + mapLib={maplibregl} + minZoom={6} + maxZoom={18} + > + {currentLocation && ( + + marker + + )} + +
+ ); +};