[PYTHON] Utilisons les données ferroviaires des informations numériques foncières nationales

Objectif

Avec le service national de téléchargement d'informations numériques foncières, vous pouvez obtenir des données gérées par le ministère des Terres, des Infrastructures, des Transports et du Tourisme. Cette fois, je vais tracer les coordonnées sur Google Map en utilisant les données ferroviaires.

** démo: ** http://needtec.sakura.ne.jp/railway_location/railway

GIT: https://github.com/mima3/railway_location

À propos des données

Les données ferroviaires peuvent être téléchargées à partir de la page suivante.

** Information numérique terrestre nationale Données ferroviaires ** http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N02-v2_2.html

Veuillez vous référer à ce qui suit pour l'utilisation de XML dans le fichier téléchargé. http://nlftp.mlit.go.jp/ksj/gml/product_spec/KS-PS-N02-v2_1.pdf

En termes simples, les données ferroviaires contiennent des informations qui montrent la forme de la ligne et des informations sur la gare. Ici, les éléments importants sont: ・ Gml: informations sur la courbe de courbe ・ Ksj: railroadSection Informations sur la section ferroviaire ・ Ksj: Informations sur la gare

Les informations de coordonnées sont stockées dans Curve. Le lien vers Curve est stocké dans l'élément d'emplacement de railroadSection et Station.

Stockage dans la base de données

Comme il est difficile de gérer une grande quantité de données en XML, elles sont temporairement stockées dans une base de données relationnelle.

À ce stade, un XML de grande taille est analysé, mais si le fichier XML entier est une fois stocké en mémoire et analysé, l'utilisation de la mémoire augmentera considérablement et il ne sera pas possible de le traiter. Par conséquent, utilisez lxml.etree.iterparse pour traiter de manière séquentielle.

Cependant, lors de l'analyse de N02-XX.xml avec lxml.etree.itreparse, une erreur se produit. C'est parce qu'il y a des lignes dans le XML qui ressemblent à ceci:

 xmlns:schemaLocation="http://nlftp.mlit.go.jp/ksj/schemas/ksj-app KsjAppSchema-N02-v2_0.xsd">

lxml considère l'URI spécifié ici comme un URI non valide et génère une erreur. Pour éviter cela, il est nécessaire de spécifier recover = True lors de l'analyse de XML dans lxml. http://stackoverflow.com/questions/18692965/how-do-i-skip-validating-the-uri-in-lxml

** Solution de contournement: **

        context = etree.iterparse(
            xml,
            events=('end',),
            tag='{http://www.opengis.net/gml/3.2}Curve',
            recover=True
        )

Dans iterparse, cet argument a été introduit après lxml == 3.4.1, vous devez donc spécifier la version pour installer lxml.

easy_install lxml==3.4.1

Sur la base de ce qui précède, le processus d'importation du XML des données ferroviaires dans la base de données est le suivant.

railway_db.py


# -*- coding: utf-8 -*-
import sqlite3
import sys
import os
# easy_install lxml==3.4.1
from lxml import etree
from peewee import *

database_proxy = Proxy()
database = None


class BaseModel(Model):
    """
Base de classe de modèle
    """
    class Meta:
        database = database_proxy


class Curve(BaseModel):
    """
Modèle d'information de courbe
    """
    curve_id = CharField(index=True, unique=False)
    lat = DoubleField()
    lng = DoubleField()


class RailRoadSection(BaseModel):
    """
Modèle d'information de section de chemin de fer
    """
    gml_id = CharField(primary_key=True)
    #Étant donné que la clé externe doit avoir une clé primaire ou une contrainte unique,
    #Elle ne peut pas être spécifiée comme clé externe pour plusieurs données.
    location = CharField(index=True)
    railway_type = IntegerField()
    service_provider_type = IntegerField()
    railway_line_name = CharField(index=True)
    operation_company = CharField(index=True)


class Station(BaseModel):
    """
Modèle d'information de la station
    """
    gml_id = CharField(primary_key=True)
    #Étant donné que la clé externe doit avoir une clé primaire ou une contrainte unique,
    #Elle ne peut pas être spécifiée comme clé externe pour plusieurs données.
    location = CharField(index=True)
    railway_type = IntegerField()
    service_provider_type = IntegerField()
    railway_line_name = CharField(index=True)
    operation_company = CharField(index=True)
    station_name = CharField(index=True)
    railroad_section = ForeignKeyField(
        db_column='railroad_section_id',
        rel_model=RailRoadSection,
        to_field='gml_id',
        index=True
    )


def setup(path):
    """
Configuration de la base de données
    @param path Chemin de la base de données
    """
    global database
    database = SqliteDatabase(path)
    database_proxy.initialize(database)
    database.create_tables([Curve, RailRoadSection, Station], True)


def import_railway(xml):
    """
National Land Numerical Institute N02-XX.Importer des informations d'itinéraire et de station à partir de XML
    TODO:
Importation inefficace de clés externes
    @chemin XML param xml
    """
    commit_cnt = 2000  #INSÉRER chaque numéro spécifié ici
    f = None
    contents = None
    namespaces = {
        'ksj': 'http://nlftp.mlit.go.jp/ksj/schemas/ksj-app',
        'gml': 'http://www.opengis.net/gml/3.2',
        'xlink': 'http://www.w3.org/1999/xlink',
        'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
    }

    with database.transaction():
        insert_buff = []
        context = etree.iterparse(
            xml,
            events=('end',),
            tag='{http://www.opengis.net/gml/3.2}Curve',
            recover=True
        )
        for event, curve in context:
            curveId = curve.get('{http://www.opengis.net/gml/3.2}id')
            print (curveId)
            posLists = curve.xpath('.//gml:posList', namespaces=namespaces)
            for posList in posLists:
                points = posList.text.split("\n")
                for point in points:
                    pt = point.strip().split(' ')
                    if len(pt) != 2:
                        continue
                    insert_buff.append({
                        'curve_id': curveId,
                        'lat': float(pt[0]),
                        'lng': float(pt[1])
                    })
                    if len(insert_buff) >= commit_cnt:
                        Curve.insert_many(insert_buff).execute()
                        insert_buff = []
        if len(insert_buff):
            Curve.insert_many(insert_buff).execute()
        insert_buff = []
        context = etree.iterparse(
            xml,
            events=('end',),
            tag='{http://nlftp.mlit.go.jp/ksj/schemas/ksj-app}RailroadSection',
            recover=True
        )
        for event, railroad in context:
            railroadSectionId = railroad.get(
                '{http://www.opengis.net/gml/3.2}id'
            )
            locationId = railroad.find(
                'ksj:location',
                namespaces=namespaces
            ).get('{http://www.w3.org/1999/xlink}href')[1:]
            railwayType = railroad.find(
                'ksj:railwayType', namespaces=namespaces
            ).text
            serviceProviderType = railroad.find(
                'ksj:serviceProviderType',
                namespaces=namespaces
            ).text
            railwayLineName = railroad.find(
                'ksj:railwayLineName',
                namespaces=namespaces
            ).text
            operationCompany = railroad.find(
                'ksj:operationCompany',
                namespaces=namespaces
            ).text
            insert_buff.append({
                'gml_id': railroadSectionId,
                'location': locationId,
                'railway_type': railwayType,
                'service_provider_type': serviceProviderType,
                'railway_line_name': railwayLineName,
                'operation_company': operationCompany
            })
            print (railroadSectionId)
            if len(insert_buff) >= commit_cnt:
                RailRoadSection.insert_many(insert_buff).execute()
                insert_buff = []
        if len(insert_buff):
            RailRoadSection.insert_many(insert_buff).execute()

        insert_buff = []
        context = etree.iterparse(
            xml,
            events=('end',),
            tag='{http://nlftp.mlit.go.jp/ksj/schemas/ksj-app}Station',
            recover=True
        )
        for event, railroad in context:
            stationId = railroad.get('{http://www.opengis.net/gml/3.2}id')
            locationId = railroad.find(
                'ksj:location', namespaces=namespaces
            ).get('{http://www.w3.org/1999/xlink}href')[1:]
            railwayType = railroad.find(
                'ksj:railwayType',
                namespaces=namespaces
            ).text
            serviceProviderType = railroad.find(
                'ksj:serviceProviderType',
                namespaces=namespaces
            ).text
            railwayLineName = railroad.find(
                'ksj:railwayLineName',
                namespaces=namespaces
            ).text
            operationCompany = railroad.find(
                'ksj:operationCompany',
                namespaces=namespaces
            ).text
            stationName = railroad.find(
                'ksj:stationName',
                namespaces=namespaces
            ).text
            railroadSection = railroad.find(
                'ksj:railroadSection',
                namespaces=namespaces
            ).get('{http://www.w3.org/1999/xlink}href')[1:]
            print (stationId)
            insert_buff.append({
                'gml_id': stationId,
                'location': locationId,
                'railway_type': railwayType,
                'service_provider_type': serviceProviderType,
                'railway_line_name': railwayLineName,
                'operation_company': operationCompany,
                'station_name': stationName,
                'railroad_section': RailRoadSection.get(
                    RailRoadSection.gml_id == railroadSection
                )
            })
            if len(insert_buff) >= commit_cnt:
                Station.insert_many(insert_buff).execute()
                insert_buff = []
        if len(insert_buff):
            Station.insert_many(insert_buff).execute()

Une fois stocké dans la base de données, le reste est facile à utiliser.

Précautions d'emploi

Les points que j'ai remarqués lors du traitement des informations numériques foncières nationales (données ferroviaires) sont décrits ci-dessous.

・ Il n'est pas possible de restreindre uniquement le nom de l'itinéraire. Par exemple, dans le cas de la «Ligne 1», «Yokohama City» peut la retenir ou «Chiba Monorail» peut la contenir. Par conséquent, il est nécessaire de préciser par «société exploitante» et «nom de l'itinéraire».

・ Le nom peut être différent de celui que vous utilisez toujours. JR East est devenu le chemin de fer de passagers de l'est du Japon et le métro de Tokyo est devenu le métro de Tokyo.

・ L'itinéraire peut être différent de celui que vous utilisez toujours. Par exemple, dans une carte d'itinéraire normale, "Tokyo" est inclus dans "Chuo Line". Cependant, "Tokyo" n'est pas inclus dans la "Ligne Chuo" en tant qu'information numérique sur les terres nationales. «Tokyo» - «Kanda» est considéré comme «Tohoku Line». Il semble que cela soit dû au fait que la section entre la gare de Tokyo et la gare de Kanda fonctionne sur une ligne dédiée posée sur la ligne principale de Tohoku.

Recommended Posts

Utilisons les données ferroviaires des informations numériques foncières nationales
Essayez d'afficher les données ferroviaires des informations numériques des terres nationales en 3D
Essayez d'importer dans la base de données en manipulant ShapeFile d'informations numériques sur les terres nationales avec Python
Utilisons les données ouvertes de "Mamebus" en Python
Faisons l'analyse des données de naufrage du Titanic comme ça
Collectons automatiquement les informations de l'entreprise (données XBRL) à l'aide de l'API EDINET (4/10)
Informations sur la carte de base à l'aide de la conversion Geotiff Python des données numériques d'élévation
Décidons le gagnant du bingo
Expliquer le mécanisme de la classe de données PEP557
Bases de la théorie de l'information quantique: compression de données (1)
Obtenez la liste des colonnes et la liste des données de CASTable
Examinons le mécanisme de la chinchirorine de Kaiji
Visualisez les données d'exportation du journal Piyo
Bases de la théorie de l'information quantique: compression de données (2)
Vérifions la transition démographique de la ville de Matsue, préfecture de Shimane avec des données ouvertes