[JAVA] J'ai essayé de créer mon propre guide de transfert en utilisant OpenTrip Planner et GTFS

introduction

J'ai créé mon propre site d'informations sur les transferts (European Continental Railway) en utilisant OpenTripPlanner (OTP) comme moteur de recherche d'itinéraire, mais uniquement avec OTP comme moteur de recherche d'itinéraire. Il semble qu'il n'y ait pas beaucoup d'informations japonaises sur la façon de l'utiliser (bien qu'il n'y ait pas beaucoup d'informations sur l'anglais ...), donc je l'ai résumé sous forme d'article. De plus, étant donné que seules les données horaires sont enregistrées sans enregistrer les données cartographiques, veuillez les utiliser comme référence pour une telle utilisation.

Qu'est-ce qu'OpenTripPlanner?

OpenTripPlanner est une application de recherche d'itinéraire intégrée open source. Il est possible de prendre des données OpenStreetMap et GTFS (décrit plus loin) et d'effectuer une recherche d'itinéraire, y compris la marche et les véhicules, ce que l'on appelle le guidage de transfert.

Qu'est-ce que GTFS

GTFS est un format pour les itinéraires de transport en commun et les informations d'exploitation créé par Google. En un mot, ce sont des données sur les horaires, et ces dernières années, les installations de transport du monde entier ont publié les données sous forme de données ouvertes.

Présentation du site créé European Continental Railway

Avant la guerre (vers 1939), quand il n'y avait pas encore de jets, il semble qu'un seul billet pouvait voyager du Japon vers l'Europe en train et en bateau. Nous créons un site de loisir particulièrement inutile et qui peut fournir des informations sur les transferts de train à ces heures. Les données sur les grandes lignes ferroviaires de Tokyo à Londres et Paris (Japon → Corée → ex-Mandchourie → ex-Union soviétique → Pologne → Allemagne → Belgique → Royaume-Uni / France) sont enregistrées en collectant les horaires des chemins de fer du monde entier. image.png

Comment utiliser OTP

Pour la méthode de lancement d'OTP en tant qu'application Web normale, reportez-vous à d'autres articles, mais cette fois je me concentrerai sur ce qui suit.

--Utilisez OTP comme une bibliothèque Java au lieu d'une application

Préparation

L'environnement de la version suivante est supposé.

--JDK 1.8 ou supérieur

Créez un projet Maven et ajoutez les informations suivantes au fichier pom.xml. J'ajoute un référentiel car OTP fait référence à différents OSS: OneBusAway et GeoTools.

pom.xml


<repositories>
  <repository>
    <id>onebusaway-releases</id>
    <name>Onebusaway Releases Repo</name>
    <url>http://nexus.onebusaway.org/content/repositories/releases/</url>
  </repository>
  <repository>
    <id>osgeo</id>
    <name>OSGeo Release Repository</name>
    <url>https://repo.osgeo.org/repository/release/</url>
  </repository>
</repositories>

<dependencies>
  <dependency>
    <groupId>org.opentripplanner</groupId>
    <artifactId>otp</artifactId>
    <version>1.4.0</version>
  </dependency>
</dependencies>

Créer un graphique

OTP stocke les routes et les itinéraires de transport et les données d'horaires dans un objet appelé Graph. Cependant, vous devez lire les données GTFS compressées et créer un objet GtfsBundle avant de créer un objet Graph. Étant donné que OTP peut lire plusieurs fichiers GTFS, il est nécessaire de donner à chaque fichier un ID unique. (La partie où feedId est défini)

python


GtfsBundle gtfsBundle = new GtfsBundle(file);
gtfsBundle.setTransfersTxtDefinesStationPaths(true);
GtfsFeedId feedId = new GtfsFeedId.Builder().id("id").build();
gtfsBundle.setFeedId(feedId);

Après avoir fusionné le GtfsBundle créé en une seule liste (gtfsBundles), enregistrez-le dans l'objet Graph.

python


GtfsModule gtfsModule = new GtfsModule(gtfsBundles);
Graph graph = new Graph();
gtfsModule.buildGraph(graph, null);
graph.summarizeBuilderAnnotations();
graph.index(new DefaultStreetVertexIndexFactory());

Recherche d'itinéraire

Tout d'abord, un objet RoutingRequest qui définit des conditions de recherche telles que le lieu de départ, le lieu d'arrivée et l'heure de départ (ou d'arrivée) est généré. Puisqu'il s'agit à l'origine d'une application de recherche d'itinéraire qui utilise des données cartographiques, il est nécessaire de spécifier les coordonnées même à la station.

python


RoutingRequest routingRequest = new RoutingRequest();
routingRequest.setNumItineraries(3); //Nombre de candidats pour les résultats de recherche
//Définir l'heure de référence pour la recherche
LocalDateTime ldt = LocalDateTime.parse("1939-08-01T13:00");
routingRequest.dateTime = ldt.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
routingRequest.setArriveBy(false); //La valeur par défaut est de spécifier l'heure de départ, mais si True, vous pouvez spécifier l'heure d'arrivée.
//Latitude de départ et d'arrivée/Spécifié par la longitude
routingRequest.from = new GenericLocation(35.3369, 139.44699); //Gare de Tsujido
routingRequest.to = new GenericLocation(35.17096, 136.88232); //Gare de Nagoya
routingRequest.ignoreRealtimeUpdates = true; //Ignorer les informations GTFS en temps réel
routingRequest.reverseOptimizing = true; //Recherchez dans la direction opposée des résultats de recherche et recherchez la dernière heure de départ
//Paramètre pour rechercher uniquement avec des données GTFS
routingRequest.setModes(new TraverseModeSet(TraverseMode.TRANSIT));
routingRequest.onlyTransitTrips = true;
//Définir l'objet graphique
routingRequest.setRoutingContext(graph);

Recherche à l'aide de l'objet RoutingReques et de l'objet Graph.

python


Router router = new Router("OTP", graph);
List<GraphPath> paths = new GraphPathFinder(router).getPaths(routingRequest);
TripPlan tripPlan = GraphPathToTripPlanConverter.generatePlan(paths, routingRequest);

Le résultat de la recherche est stocké dans l'objet TripPlan. L'objet Itinerary candidat du résultat de la recherche est stocké dans la Liste obtenue par la propriété d'itinéraire de TripPlan. L'objet Leg de l'information d'itinéraire est stocké dans la liste obtenue par la propriété leg de Itinerary dans l'ordre de transfert.

Résumé

Résultats de recherche

Août 1939 en utilisant les données GTFS (données horaires de la ligne Tokaido en décembre 1939, il y a environ 80 ans) sur mon site European Continental Railway Explorons l'itinéraire de la gare de Tsujido à la gare de Nagoya à 13h00 le 1er. Le résultat est le suivant. Si vous manquez le train à 13h08 (soit dit en passant, le train régulier à destination de Yonehara), il semble que vous arriverez à Nagoya plus tôt si vous revenez à Yokohama et Ofuna et prenez le train express limité Sakura Shimonoseki et express Osaka. Vous pouvez consulter les horaires de chaque gare en 1939 en cliquant sur le nom de la gare sur chaque plan d'itinéraire dans European Continental Railway. (Gare de Tsujido, Gare de Yokohama, Gare d'Ofuna)

13:08 Départ- 18:28 arrivée(320 minutes)
>Tsujido 13:08 départ-Numazu 14:46 arrivées
>Numazu 15:04 départs-Nagoya 18:28 arrivées
13:18 Départ- 19:00 arrivée(342 minutes)
>Tsujido 13:18 coups-Yokohama 13:50 arrivées
>Yokohama 13:58 départs-Nagoya 19:00 arrivée
14:08 Départ- 19:38 arrivée(330 minutes)
>Tsujido 14:08 départ-Ofuna 14:20 arrivées
>Ofuna 14:26 coups-Nagoya 19:38 arrivées

Tout le code source

Le texte complet de cette source ressemble à ceci.

OtpTest.java


import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.opentripplanner.api.model.TripPlan;
import org.opentripplanner.api.resource.GraphPathToTripPlanConverter;
import org.opentripplanner.common.model.GenericLocation;
import org.opentripplanner.graph_builder.model.GtfsBundle;
import org.opentripplanner.graph_builder.module.GtfsFeedId;
import org.opentripplanner.graph_builder.module.GtfsModule;
import org.opentripplanner.model.FeedScopedId;
import org.opentripplanner.model.Route;
import org.opentripplanner.model.Stop;
import org.opentripplanner.model.Trip;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.edgetype.TripPattern;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.GraphIndex;
import org.opentripplanner.routing.impl.DefaultStreetVertexIndexFactory;
import org.opentripplanner.routing.impl.GraphPathFinder;
import org.opentripplanner.routing.spt.GraphPath;
import org.opentripplanner.standalone.Router;

public class OtpTest {

	public static void main(String[] args) {

		//Chemin du dossier de sauvegarde du fichier GTFS
		String in = "";

		//Lire tous les fichiers ZIP du dossier sous forme de fichiers GTFS
		List<GtfsBundle> gtfsBundles = null;
		try (final Stream<Path> pathStream = Files.list(Paths.get(in))) {
			gtfsBundles = pathStream.map(Path::toFile).filter(file -> file.getName().toLowerCase().endsWith(".zip"))
					.map(file -> {
						GtfsBundle gtfsBundle = new GtfsBundle(file);
						gtfsBundle.setTransfersTxtDefinesStationPaths(true);
						//Puisqu'il est nécessaire de donner à chaque fichier GTFS un ID unique, utilisez le nom de fichier comme ID.
						String id = file.getName().substring(0, file.getName().length() - 4);
						gtfsBundle.setFeedId(new GtfsFeedId.Builder().id(id).build());
						return gtfsBundle;
					}).collect(Collectors.toList());
		} catch (final IOException e) {
			throw new RuntimeException(e);
		}

		//Traitement pour enregistrer le fichier GTFS lu dans l'objet Graph
		GtfsModule gtfsModule = new GtfsModule(gtfsBundles);
		Graph graph = new Graph();
		gtfsModule.buildGraph(graph, null);
		graph.summarizeBuilderAnnotations();
		graph.index(new DefaultStreetVertexIndexFactory());
		
		//Le graphique peut également être sérialisé et enregistré
		// graph.save(file);

		//Paramètres des conditions de recherche
		RoutingRequest routingRequest = new RoutingRequest();
		routingRequest.setNumItineraries(3); //Nombre de candidats pour les résultats de recherche
		//Définir l'heure de référence pour la recherche
		LocalDateTime ldt = LocalDateTime.parse("1939-08-01T13:00");
		routingRequest.dateTime = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()).getTime() / 1000;
		routingRequest.setArriveBy(false); //La valeur par défaut est de spécifier l'heure de départ, mais si True, vous pouvez spécifier l'heure d'arrivée.
		//Latitude de départ et d'arrivée/Spécifié comme doux
		routingRequest.from = new GenericLocation(35.3369, 139.44699); //Gare de Tsujido
		routingRequest.to = new GenericLocation(35.17096, 136.88232); //Gare de Nagoya
		routingRequest.ignoreRealtimeUpdates = true; //Ignorer les informations GTFS en temps réel
		routingRequest.reverseOptimizing = true; //Recherchez dans la direction opposée des résultats de recherche et recherchez la dernière heure de départ
		//Paramètre pour rechercher uniquement avec des données GTFS
		routingRequest.setModes(new TraverseModeSet(TraverseMode.TRANSIT));
		routingRequest.onlyTransitTrips = true;
		//Définir l'objet graphique
		routingRequest.setRoutingContext(graph);

		//Traitement de la recherche
		Router router = new Router("OTP", graph);
		List<GraphPath> paths = new GraphPathFinder(router).getPaths(routingRequest);
		TripPlan tripPlan = GraphPathToTripPlanConverter.generatePlan(paths, routingRequest);

		//Afficher les résultats de la recherche
		tripPlan.itinerary.forEach(p -> {
			//Informations récapitulatives pour chaque candidat d'itinéraire
			System.out.println(String.format("%d:%02j départ- %d:%02j arrivée(%d minutes)", 
					p.startTime.get(Calendar.HOUR_OF_DAY), p.startTime.get(Calendar.MINUTE), 
					p.endTime.get(Calendar.HOUR_OF_DAY), p.endTime.get(Calendar.MINUTE), 
					(p.duration / 60)));
			p.legs.forEach(l -> {
				//Informations sur chaque train de l'itinéraire
				System.out.println(String.format("> %s %d:%02j départ- %s %d:%02j arrivée", 
						l.from.name, l.startTime.get(Calendar.HOUR_OF_DAY), l.startTime.get(Calendar.MINUTE), 
						l.to.name, l.endTime.get(Calendar.HOUR_OF_DAY), l.endTime.get(Calendar.MINUTE)));
			});
		});
	}
}

De plus, à partir de l'objet Leg, si les informations de dessin de l'itinéraire sont stockées dans GTFS forms.txt, elles peuvent être obtenues sous la forme d'une chaîne de caractères codée GEO avec l.legGeometry.getPoints ().

Recommended Posts

J'ai essayé de créer mon propre guide de transfert en utilisant OpenTrip Planner et GTFS
J'ai créé et défini mon propre dialecte avec Thymeleaf et j'ai essayé de l'utiliser
J'ai essayé de faire coexister Java Optional et la clause de garde
J'ai essayé de résumer l'orientation de l'objet à ma manière.
J'ai essayé d'intégrer parfaitement Docker et Maven / Netbean en utilisant Jib
[Unity] J'ai essayé de créer un plug-in natif UniNWPathMonitor en utilisant NWPathMonitor
J'ai essayé d'en faire une URL arbitraire en utilisant l'imbrication de routage
J'ai essayé de créer une simple application Android de reconnaissance faciale en utilisant OpenCV
J'ai créé un bot de transaction d'arbitrage de monnaie virtuelle et essayé de gagner de l'argent
J'ai essayé de créer une application de conversation en Java à l'aide de l'IA «A3RT»
J'ai essayé de faire une authentification de base avec Java
Une histoire où j'ai essayé de faire une vidéo en liant Traitement et Resolume
[Android] J'ai quitté SQLite et essayé d'utiliser Realm
J'ai essayé de lier JavaFX et Spring Framework.
J'ai essayé d'implémenter un serveur en utilisant Netty
[JDBC ③] J'ai essayé d'entrer à partir de la méthode principale en utilisant des espaces réservés et des arguments.
J'ai essayé d'utiliser Wercker pour créer et publier une image Docker qui lance GlassFish 5
J'ai essayé de faire fonctionner SQS en utilisant AWS Java SDK
J'ai démarré MySQL 5.7 avec docker-compose et j'ai essayé de me connecter
J'ai essayé d'intégrer le bouton AWS I oT et Slack
J'ai essayé de créer une fonction de connexion avec Java
J'ai essayé d'utiliser YOLO v4 sur Ubuntu et ROS
J'ai essayé de mâcher C # (lire et écrire des fichiers)
J'ai essayé d'utiliser Gson
J'ai essayé d'utiliser TestNG
J'ai essayé d'utiliser Galasa
J'ai été piégé lorsque j'ai généré mes propres égaux de classe et hashCode en Java à l'aide de l'IDE
J'ai essayé de créer une application qui vous permet de publier et de discuter par genre ~ Vue d'ensemble de l'application ~
Facile à créer LINE BOT avec Java Servlet Partie 2: J'ai essayé des messages image et des modèles
J'ai essayé un test unitaire de l'application Rails en utilisant RSpec et FactoryBot
J'ai essayé de faire une demande en 3 mois d'inexpérimenté
J'ai essayé de résumer les bases de kotlin et java
J'ai essayé de faire de Ben figure une animation GIF facile à comprendre
J'ai essayé de vérifier ceci et celui de Spring @ Transactional
Je veux faire une liste avec kotlin et java!
Je veux créer une fonction avec kotlin et java!
J'ai essayé de construire l'environnement petit à petit en utilisant docker
J'ai essayé de résumer les applications et les outils de développement personnellement utiles (outils de développement)
J'ai essayé de résumer les applications et les outils de développement personnellement utiles (Apps)
J'ai essayé d'utiliser Dapr en Java pour faciliter le développement de microservices
J'ai créé un client RESAS-API en Java
J'ai essayé de démarrer avec Swagger en utilisant Spring Boot
[Rails] Implémentation de la fonction de catégorie multicouche en utilisant l'ascendance "J'ai essayé de créer une fenêtre avec Bootstrap 3"
[Spring Boot] Je veux ajouter mon propre fichier de propriétés et obtenir la valeur avec env.getProperty ().
Après avoir appris Progate, j'ai essayé de créer une application SNS en utilisant Rails dans l'environnement local
Lorsque j'ai essayé d'exécuter mon propre service, il a échoué, alors je l'ai vissé dans le planificateur de tâches
J'ai essayé d'utiliser azure cloud-init
J'ai essayé d'utiliser Apache Wicket
J'ai essayé d'utiliser Java REPL
J'ai essayé de vérifier yum-cron
[Metal] J'ai essayé de comprendre le flux jusqu'au rendu avec Metal
Je veux juste écrire Java en utilisant Eclipse sur mon Mac
J'ai essayé de me connecter à MySQL en utilisant le modèle JDBC avec Spring MVC
J'ai essayé d'approfondir ma compréhension de l'orientation des objets de n%
J'ai essayé de créer une application simple en utilisant Dockder + Rails Scaffold