npx create-react-app ****でアプリを作成
インストール
ターミナル
npm install firebase
npm install "@vis.gl/react-google-maps"
npm install "@googlemaps/react-wrapper"
npm install react-router-dom
#firebase.jsを設定
firebase.js
// firebase.js
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore"; // Firestore をインポート
const firebaseConfig = {
apiKey: "AIzaSyCyO*******",
authDomain: "react--map-64573.firebaseapp.com",
projectId: "react--map-64573",
storageBucket: "react--map-64573.appspot.com",
messagingSenderId: "289752151691",
appId: "1:28******:web:3e8385*************",
measurementId: "G-V********",
};
// Firebase を初期化
const app = initializeApp(firebaseConfig);
const db = getFirestore(app); // Firestore インスタンスを作成
export { db }; // db をエクスポート
google map APIを取得
.env (拡張子なし)ファイルを作成し、最上部に配置
.env
REACT_APP_GOOGLE_MAPS_API_KEY=あなたのAPIキ
gitignoreにつ追加
# misc
.DS_Store
.env
.env.local
場所を指定しfirebaseに保存
GoogleMapSelect.js
// GoogleMapSelect
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { APIProvider } from "@vis.gl/react-google-maps";
import GoogleMapSelect from "./Components/GoogleMapSelect";
const App = () => (
<APIProvider apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<Router>
<Routes>
{/* 他のルートをここに追加できます */}
<Route path="/" element={<GoogleMapSelect />} />
</Routes>
</Router>
</APIProvider>
);
export default App;
選択して位置を保存するコード
GoogleMapSelect.js
// GoogleMapSelect.js
import React from "react";
import { Wrapper } from "@googlemaps/react-wrapper";
import { collection, addDoc } from "firebase/firestore";
import { db } from "../firebase"; // Firebaseの設定ファイルをインポート
import "./GoogleMapSelect.css";
/**
* Google Mapsの読み込み状態を表示
* @param {string} status - Google Mapsの読込状態。"FAIL"は、読み込み失敗メッセージを表示。
* @returns {JSX.Element} 読み込み状態に応じたメッセージを含むJSX要素
*/
const render = (status) => {
if (status === "FAIL") {
return <h1>Google Mapsの読み込みに失敗しました</h1>;
}
return <h1>{status}</h1>;
};
/**
* GoogleMapSelectコンポーネント
* @returns
*/
const GoogleMapSelect = () => {
const [center, setCenter] = React.useState({ lat: 35.0116, lng: 135.7681 }); // 初期位置を京都に設定
const [zoom, setZoom] = React.useState(15); // ズームレベルの初期値
/**
* 地図上の中心位置を更新
*/
const updateCenter = (map) => {
if (map) {
const center = map.getCenter().toJSON();
setCenter(center);
setZoom(map.getZoom()); // ズームレベルも更新
}
};
/**
* Firestoreに位置情報を保存する関数
*/
const saveLocation = async () => {
if (zoom < 19) {
alert("もう少し拡大して位置を指定してください。");
return;
}
try {
// 現在の地図の中心を位置情報として保存
const position = {
lat: center.lat,
lng: center.lng,
timestamp: new Date(),
};
// Firestoreに位置情報を保存
const docRef = await addDoc(collection(db, "locations"), position);
console.log("Location saved to database with ID:", docRef.id);
} catch (error) {
console.error("Error saving location to database", error);
}
};
/**
* 地図表示の際の初期値を設定
*/
const mapOptions = {
center: center,
zoom: zoom, // 動的にズームレベルを設定
styles: [],
};
return (
<div className="container" style={{ display: "flex", height: "100vh" }}>
<div className="map-title">地図の世界</div>
<div className="content">
<Wrapper
apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}
render={render}
>
<div
className="content-map"
style={{ width: "100%", height: "100%" }}
>
<Map
options={mapOptions}
onCenterChange={updateCenter} // 地図の中心が変わったときに更新
style={{ width: "100%", height: "100%" }}
>
<Marker position={center} />
</Map>
{/* 地図の中心に四角形を表示 */}
<div className="center-square"></div>
<div className="center-square2"></div>
</div>
</Wrapper>
<button className="save-button" onClick={saveLocation}>
この地点を指定する
</button>
</div>
</div>
);
};
/**
* 様々な引数に基づき、googleMapを表示
* @param {*} param0
* @returns
*/
const Map = ({ onCenterChange, children, style, options, ...otherProps }) => {
const ref = React.useRef(null); //dom情報を設定
const [map, setMap] = React.useState(null); //Mapに関する各種情報を保持
/**
* dom情報があって、map情報がない場合、新しい地図を表示
*/
React.useEffect(() => {
if (ref.current && !map) {
const newMap = new window.google.maps.Map(ref.current, options);
setMap(newMap);
}
}, [ref, map, options]);
React.useEffect(() => {
if (map) {
map.setOptions(options);
}
}, [map, options]);
React.useEffect(() => {
if (map) {
window.google.maps.event.clearListeners(map, "idle");
// `idle` イベントリスナーを設定
map.addListener("idle", () => {
if (onCenterChange) {
onCenterChange(map);
}
});
}
}, [map, onCenterChange]);
return (
<>
<div ref={ref} style={style} />
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { map });
}
})}
</>
);
};
const Marker = ({ position, map }) => {
const [marker, setMarker] = React.useState(null);
React.useEffect(() => {
if (!marker) {
setMarker(new window.google.maps.Marker());
}
return () => {
if (marker) {
marker.setMap(null);
}
};
}, [marker]);
React.useEffect(() => {
if (marker && map) {
marker.setPosition(position);
marker.setMap(map);
}
}, [marker, position, map]);
return null;
};
export default GoogleMapSelect;
CSSの例
/* GOogleMapSelect.css */
html, body {
height: 100%;
margin: 0; /* マージンをゼロに設定 */
}
#root {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
flex-direction: column; /* 縦方向に配置 */
width: 1000px;
height: 1500px; /* 高さも設定することをお勧めします */
position: relative;
margin-top: 20px;
}
.content {
width: 90%;
height: 800px !important;
margin: 10px auto;
border: solid 1px blue;
position: relative;
}
.content button {
position: absolute;
margin: 24px 0;
right: 0;
border: none;
border-radius: 5px;
padding: 10px 20px;
background-color: #4CAF50;
color: white;
font-size: 18px;
font-weight: 600;
cursor: pointer;
}
.map-title {
font-size: 30px;
font-weight: 600;
padding: 5px 10px 0 0;
text-align: center;
width: 90%; /* フル幅を指定 */
margin: 10px auto;
background-color: blue;
color: white;
}
.content-map {
position: relative; /* 四角形を中央に配置するために必要 */
width: 100%;
height: 100%;
}
.center-square {
position: absolute;
width: 30px;
height: 30px;
background-color: transparent;
border: solid 2px red;
transform: translate(-50%, -50%); /* 中心に合わせるための調整 */
top: 50%; /* 縦の中心 */
left: 50%; /* 横の中心 */
z-index: 10; /* 地図の上に表示されるようにする */
}
.center-square2 {
position: absolute;
background-color: transparent;
width: 38px;
height: 38px;
border: solid 2px red;
transform: translate(-50%, -50%); /* 中心に合わせるための調整 */
top: 50%; /* 縦の中心 */
left: 50%; /* 横の中心 */
z-index: 9; /* 地図の上に表示されるようにする */
}
保存するファイルを設定
MapComponent.js
import React, { useState } from "react";
import { APIProvider, Map, Marker } from "@vis.gl/react-google-maps";
import {
getFirestore,
doc,
setDoc,
collection,
addDoc,
} from "firebase/firestore";
import { db } from "../firebase";
const MapComponent = () => {
const [markerPosition, setMarkerPosition] = useState(null);
const handleMapClick = async (event) => {
console.log("Map click event:", event);
const latLng = event.detail?.latLng;
if (latLng && latLng.lat && latLng.lng) {
const position = {
lat: latLng.lat,
lng: latLng.lng,
timestamp: new Date(), // 現在の日付を追加
};
setMarkerPosition(position);
try {
// ドキュメントの ID を自動生成
const docRef = await addDoc(collection(db, "locations"), position);
console.log("Location saved to database with ID:", docRef.id);
} catch (error) {
console.error("Error saving location to database", error);
}
} else {
console.error("Event does not contain latLng");
}
};
return (
<Map
style={{ width: "100%", height: "400px" }}
defaultCenter={{ lat: 35.658584, lng: 139.745433 }}
defaultZoom={15}
onClick={handleMapClick}
>
{markerPosition && <Marker position={markerPosition} />}
</Map>
);
};
export default MapComponent;
表示するファイルを設置
MapRedirectComponent.js
import React, { useEffect } from "react";
import { useParams } from "react-router-dom";
import { doc, getDoc } from "firebase/firestore";
import { db } from "../firebase";
const MapRedirectComponent = () => {
const { locationId } = useParams();
const [locationData, setLocationData] = React.useState(null);
useEffect(() => {
const fetchLocation = async () => {
if (locationId) {
try {
const docRef = doc(db, "locations", locationId);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
setLocationData(docSnap.data());
} else {
console.log("No such document!");
}
} catch (error) {
console.error("Error fetching location data", error);
}
}
};
fetchLocation();
}, [locationId]);
useEffect(() => {
if (locationData) {
const { lat, lng } = locationData;
const googleMapsUrl = `https://www.google.com/maps?q=${lat},${lng}`;
window.location.href = googleMapsUrl; // 自動的にリダイレクト
}
}, [locationData]);
return <p>Redirecting...</p>; // リダイレクト中のメッセージ
};
export default MapRedirectComponent;
App.js でルーティング
react domがインストールできていること
App.js
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { APIProvider } from "@vis.gl/react-google-maps";
import MapComponent from "./components/MapComponent"; // パスを確認
import DisplayMapComponent from "./components/DisplayMapComponent"; // パスを確認
const App = () => (
<APIProvider apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<Router>
<Routes>
<Route path="/map" element={<MapComponent />} />
<Route path="/displaymap" element={<DisplayMapComponent />} />
<Route
path="/displaymap/:locationId"
element={<DisplayMapComponent />}
/>
{/* 他のルートをここに追加できます */}
</Routes>
</Router>
</APIProvider>
);
export default App;
Home.jsを作成
home.js
import { collection, getDocs } from "firebase/firestore";
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { db } from "../firebase";
const Home = () => {
const [locations, setLocations] = useState([]);
useEffect(() => {
const fetchLocations = async () => {
try {
const querySnapshot = await getDocs(collection(db, "locations"));
const locationsData = querySnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setLocations(locationsData);
} catch (error) {
console.error("Error fetching locations data", error);
}
};
fetchLocations();
}, []);
return (
<>
<div>Home</div>
<div>
<Link to="/map">地図表示</Link>
</div>
<div>
{locations.length > 0 ? (
<ul>
{locations.map((location) => (
<li key={location.id}>
<Link to={`/displaymap/${location.id}`}>
<button>{location.id}</button>
</Link>
</li>
))}
</ul>
) : (
<p>Loading location data...</p>
)}
</div>
</>
);
};
export default Home;
App.jsを変更
App.js
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { APIProvider } from "@vis.gl/react-google-maps";
import MapComponent from "./components/MapComponent"; // パスを確認
import DisplayMapComponent from "./components/DisplayMapComponent"; // パスを確認
import Home from "./components/Home";
const App = () => (
<APIProvider apiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY}>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/map" element={<MapComponent />} />
<Route path="/displaymap" element={<DisplayMapComponent />} />
<Route
path="/displaymap/:locationId"
element={<DisplayMapComponent />}
/>
{/* 他のルートをここに追加できます */}
</Routes>
</Router>
</APIProvider>
);
export default App;