2
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ベースのルート検索アプリを作成してみる

Last updated at Posted at 2024-04-22

※ 記事内のコードを最新のHARP engine対応に変更しました。

はじめに

HEREでエンジニアをしておりますkekishidaと申します。
以前、拙記事においてHERE Maps API for Javascripを使用してReactベースで簡単な地図アプリ作成方法の紹介をしました。

今回はこちらの記事で紹介したサンプルをベースにルート検索アプリを作成してみたいと思います。
完成イメージは以下の通りです。
それぞれのテキストボックス(origin/destination)に出発地及び目的地の名前を入力し、Calculate ROUTEボタンをクリックすることで、ルート検索されます。
image.png
image.png
Zoom levelを上げてみると、初台南ランプ(東京)より高速に入り、
image.png
森ノ宮ランプ(大阪)より高速から降りている経路となっていることがわかります。
image.png

今回使用するプロダクトおよびサービスは以下の通りです。

  • HERE Maps API for Javascript

  • HERE Geocoding & Search

  • HERE Routing v8

それでは始めましょう。

必要なもの

ご紹介するサンプルコードは、HEREアカウントの取得が必要になります。

HEREアカウントの取得

以下の記事が大変参考になります。

作成手順 (step by step)

まずは、以下拙記事のサンプルコードを実装してください。

Routing.jsの作成

新規コンポーネントRouting.jsを作成します。こちらのコンポーネントでは出発地、目的地をそれぞれ入力して、Geocoding&Search APIとRouting APIを組み合わせてルート検索結果を求めます。

Routing.js
import * as React from 'react';
import { useState } from 'react';


const Routing = (props) => {
  const [text1,setText1] = useState("");
  const [text2,setText2] = useState("");

  const apikey = props.apikey;

  const H = window.H;
  const platform = new H.service.Platform({
      apikey: apikey
  });
  const searchService = platform.getSearchService();
  const routingService = platform.getRoutingService(null, 8);
  const routingFunction =(origin, destination) => {
      let onError = (error) => {
          alert(error.message);
      }
      let onResult = function(result) {
        if (result.routes.length) {
          result.routes[0].sections.forEach((section) => {
              props.onRoutingResult(section.polyline);
          });
        }
      }
      let routingParameters = {
        'transportMode': 'car',
        'return': 'polyline'
      };
      let calculateRoute = () => {
        if (!origin || !destination) return;
        routingParameters.origin = origin.lat+","+origin.lng;
        routingParameters.destination = destination.lat+","+destination.lng;
        routingService.calculateRoute(routingParameters, onResult, onError);
      }
      calculateRoute();
  }
  const onClickRouting = () => {
      searchService.geocode({
        q: text1,
        limit: 1
      }, (result) => {
        result.items.forEach((item) => {
          console.log(item.position);
          let origin = item.position;
          props.onOriginResult(origin);
          searchService.geocode({
            q: text2,
            limit: 1
          }, (result) => {
            result.items.forEach((item) => {
              console.log(item.position);
              let destination = item.position;
              props.onDestinationResult(destination);
              routingFunction(origin,destination);
            });
          }, alert);
            });
      }, alert);
      setText1("");
      setText2("");
  }


  return (
    <div >
        <div >
          <label>
            Origin : 
          </label>
          &nbsp;
          <input type="text" name="origin" id="query1" value={text1}
              onChange={(event)=>setText1(event.target.value)}
              placeholder="Origin location" />
          &emsp;
          <label>
            Destination :
          </label>
          &nbsp;
          <input type="text" name="destination" id="query2" value={text2}
              onChange={(event)=>setText2(event.target.value)}
              placeholder="Destination location" />
        </div>
        <div>
          <button onClick={onClickRouting}>
              Calculate ROUTE!
          </button>
        </div>
    </div>
  );
};
export default Routing;

Routing APIにより取得したい情報をコントロールする方法は、以下のコード部分になります。
(こちらについては、次回投稿予定の記事についてより詳細を解説したいと多います。)

      let onResult = function(result) {
        if (result.routes.length) {
          result.routes[0].sections.forEach((section) => {
              props.onRoutingResult(section.polyline);
          });
        }
      }
      let routingParameters = {
        'transportMode': 'car',
        'return': 'polyline'
      };

Map.jsの修正

Routing APIが算出した経路情報を地図上に表示するための処理を追加します。

Map.js
import * as React from 'react';

const Map = (props) => {
  const apikey = props.apikey;
-  const gps = props.gps;
+  const gps = props.origin;
+  const destination = props.destination;
+  const polyline = props.polyline;
  // Create a reference to the HTML element we want to put the map on
  const mapRef = React.useRef(null);
  /**
   * Create the map instance
   * While `useEffect` could also be used here, `useLayoutEffect` will render
   * the map sooner
   */
   React.useLayoutEffect(() => {
    // `mapRef.current` will be `undefined` when this hook first runs; edge case that
    if (!mapRef.current) return;
    const H = window.H;
    const platform = new H.service.Platform({
        apikey: apikey
    });
    const defaultLayers = platform.createDefaultLayers({
      engineType: H.Map.EngineType.HARP
    });
    const engineType = H.Map.EngineType["HARP"];
    // configure an OMV service to use the `core` endpoint
    const omvService = platform.getOMVService({ 
      path: "v2/vectortiles/core/mc", 
      // Request the transit vector layer
      queryParams: {
        content: "default,transit"
      },
    });
    const baseUrl = `https://js.api.here.com/v3/3.1/styles/harp/oslo`;

    // create a Japan specific style
    const style = new H.map.render.harp.Style(`${baseUrl}/tko.normal.day.json`);
    // instantiate provider and layer for the base map
    const omvProvider = new H.service.omv.Provider(omvService, style, {
      engineType,
      lg: "ja",
    });
    var omvlayer = new H.map.layer.TileLayer(omvProvider, { max: 22 ,dark:true});

    // instantiate (and display) a map:
    var map = new H.Map(mapRef.current, omvlayer, {
        zoom: 16,
        center: { lat: gps.lat, lng: gps.lng },
        engineType,
    });

    // MapEvents enables the event system
    // Behavior implements default interactions for pan/zoom (also on mobile touch environments)
    var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
    // Create the default UI components
    var ui = H.ui.UI.createDefault(map, defaultLayers); 

    // Marker code goes here
    var LocationOfMarker = { lat: gps.lat, lng: gps.lng };
    var pngIcon = new H.map.Icon('<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2c2.131 0 4 1.73 4 3.702 0 2.05-1.714 4.941-4 8.561-2.286-3.62-4-6.511-4-8.561 0-1.972 1.869-3.702 4-3.702zm0-2c-3.148 0-6 2.553-6 5.702 0 3.148 2.602 6.907 6 12.298 3.398-5.391 6-9.15 6-12.298 0-3.149-2.851-5.702-6-5.702zm0 8c-1.105 0-2-.895-2-2s.895-2 2-2 2 .895 2 2-.895 2-2 2zm10.881-2.501c0-1.492-.739-2.83-1.902-3.748l.741-.752c1.395 1.101 2.28 2.706 2.28 4.5s-.885 3.4-2.28 4.501l-.741-.753c1.163-.917 1.902-2.256 1.902-3.748zm-3.381 2.249l.74.751c.931-.733 1.521-1.804 1.521-3 0-1.195-.59-2.267-1.521-3l-.74.751c.697.551 1.141 1.354 1.141 2.249s-.444 1.699-1.141 2.249zm-16.479 1.499l-.741.753c-1.395-1.101-2.28-2.707-2.28-4.501s.885-3.399 2.28-4.5l.741.752c-1.163.918-1.902 2.256-1.902 3.748s.739 2.831 1.902 3.748zm.338-3.748c0-.896.443-1.698 1.141-2.249l-.74-.751c-.931.733-1.521 1.805-1.521 3 0 1.196.59 2.267 1.521 3l.74-.751c-.697-.55-1.141-1.353-1.141-2.249zm16.641 14.501c0 2.209-3.581 4-8 4s-8-1.791-8-4c0-1.602 1.888-2.98 4.608-3.619l1.154 1.824c-.401.068-.806.135-1.178.242-3.312.949-3.453 2.109-.021 3.102 2.088.603 4.777.605 6.874-.001 3.619-1.047 3.164-2.275-.268-3.167-.296-.077-.621-.118-.936-.171l1.156-1.828c2.723.638 4.611 2.016 4.611 3.618z"/></svg>', { size: { w: 56, h: 56 } });

    // Create a marker using the previously instantiated icon:
    var marker = new H.map.Marker(LocationOfMarker, { icon: pngIcon });

    // Add the marker to the map:
    map.addObject(marker);
    //Zooming so that the marker can be clearly visible
    map.setZoom(16)   
+    if(polyline) {
+      let linestring = H.geo.LineString.fromFlexiblePolyline(polyline);
+      let routeLine = new H.map.Polyline(linestring, {
+        style: { strokeColor: 'blue', lineWidth: 8 }
+      });
+      let startMarker = new H.map.Marker(gps);
+      let endMarker = new H.map.Marker(destination);
+      map.addObjects([routeLine, startMarker,endMarker]);
+      map.getViewModel().setLookAtData({bounds: routeLine.getBoundingBox()});
+    }
    // This will act as a cleanup to run once this hook runs again.
    // This includes when the component un-mounts
    return () => {
      map.dispose();
    };
-  }, [props.gps]); // This will run this hook every time this ref is updated
+  }, [props.polyline]); // This will run this hook every time this ref is updated
  return <div style={ { width: "100%", height: "500px" } }  ref={mapRef}  />;
};
export default Map;

App.jsの編集

最後にApp.jsを修正します。

App.js
import Map from './Map';
import { useState } from 'react';
-import Search from './Search';
+import Routing from './Routing';
let apikey = <APIKEY>;

function App() {
+    const [ origin, setOrigin ] = useState({lat: "35.6814568602531", lng: "139.76799772026422"});
+    const [ destination, setDestination ] = useState({lat: "35.6814568602531", lng: "139.76799772026422"});
+    const [polyline, setPolyline] = useState(null);
    return (
    <div>
-      <Search apikey={apikey} onGeocodeResult={query=> setGps(query)}/>
-      <Map apikey={apikey} gps={gps}/>
+      <Routing apikey={apikey} onOriginResult={query=> setOrigin(query)} onDestinationResult={query=> setDestination(query)} onRoutingResult={query=> setPolyline(query)}/>
+      <Map apikey={apikey} origin={origin} destination={destination} polyline={polyline}/>
    </div>
  );
};

export default App;

プロジェクトの実行

npm start

routing.gif

おわりに

いかがでしたでしょうか?今回はReactベースの地図アプリにルート検索機能を追加してみました。本記事では最も基本的なRouting API v8の使用方法紹介のみとなっておりますが、本APIは単純なルート検索以外にも、非常に多くの機能を備えております。次回投稿予定の記事では、近々リリースを予定しております日本向けの高速道路料金計算について、今回紹介しましたコードベースに高速道路料金計算機能を追加したうえで、その解説をしたいと考えております。ここまで読んでいただいてありがとうございました。

2
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
2
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?