0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

reactにgoogle map機能を実装

Posted at

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;

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?