[JAVA] Créons Force-Graph du réseau ferroviaire à partir des données ouvertes des transports publics de Tokyo

Défi des données ouvertes des transports publics de Tokyo

Dans Tokyo Public Transportation Open Data Challenge, les principales données sur les transports publics de la zone métropolitaine sont publiées sous forme de données ouvertes, et le «3rd Tokyo Public Transportation Open Data Challenge» Si vous entrez, un jeton d'accès sera émis et vous pourrez utiliser l'API. J'ai appris à connaître le site pendant GW et j'ai essayé d'entrer pour le moment.

Aperçu

Comme je vis en milieu rural, je ne connais pas grand-chose au réseau ferroviaire de la région métropolitaine. Pour cette raison, le chemin de fer de la région métropolitaine qui combine l'organisation de l'information et l'étude de l'API Tokyo Open Data Challenge et D3.js. J'ai fait un net Force-Graph.

Contenu / procédures

1. Organisez les données avec l'API ODPT Train

Nous avons collecté des informations sur l'itinéraire et la gare avec "ODPT Train API" et organisé les données avec java. Le code source est indiqué ci-dessous. Le client http utilise OkHttp et le traitement JSON utilise Gson. Une instance Railline est générée en extrayant le "nom de la route", "l'identifiant unique" et la "station de route" de la route à partir de la réponse (JSON) de l '"ODPT Train API", et le "nom de la station" et "l'identifiant unique" de la station dans la route. , "L'instance de station est générée à partir de" l'identifiant d'itinéraire associé ". Les stations semblent se voir attribuer des identifiants uniques pour chaque ligne, de sorte que les informations sont agrégées par «nom de station (nom japonais)».

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class Test {
	private static final String URL_TOKYO_CH="https://api-tokyochallenge.odpt.org/api/v4/";
	private static final String KEY_TOKYO_CH="Jeton d'accès";

	@SuppressWarnings({ "unused", "rawtypes", "unchecked" })
	public static void main(String[] args){
        OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
        okHttpBuilder.connectTimeout(20, TimeUnit.SECONDS);
        okHttpBuilder.readTimeout(20, TimeUnit.SECONDS);
        okHttpBuilder.writeTimeout(20, TimeUnit.SECONDS);
        OkHttpClient client=okHttpBuilder.build();
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        List<Map> list=trainAPI(client,gson,"odpt:Railway");
        Map<String,Station> stations=new HashMap<String,Station>();
        List<RailLine> raillines=new ArrayList<RailLine>();
    	for(Map map : list){
    		RailLine line=new RailLine();
    		line.name_ja=((Map)map.get("odpt:railwayTitle")).get("ja").toString();
    		line.name_en=((Map)map.get("odpt:railwayTitle")).get("en").toString();
    		line.sameAs=map.get("owl:sameAs").toString();
    		line.operator=map.get("odpt:operator").toString();
    		List<Map> ll=(List<Map>)map.get("odpt:stationOrder");
    		for(Map o : ll){
    			String st=((Map)o.get("odpt:stationTitle")).get("ja").toString();
    			line.stations.add(st);
    			if(stations.containsKey(st)){
    				Station s=stations.get(st);
    				s.lines.add(line.name_ja);
    			}else{
    				Station s=new Station();
    				s.sameAs=o.get("owl:sameAs").toString();
    				s.name_ja=((Map)o.get("odpt:stationTitle")).get("ja").toString();
    				s.name_en=((Map)o.get("odpt:stationTitle")).get("en").toString();
    				s.lines.add(line.sameAs);
    				stations.put(s.name_ja, s);
    			}
    		}
    		raillines.add(line);
    	}
    	Map<String,Object> ret=new HashMap<String,Object>();
    	ret.put("stations", stations);
    	ret.put("raillines", raillines);
        File f=new File("railway.json");
        BufferedWriter bw=null;
        try{
        	bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f),"UTF-8"));
        	bw.write(gson.toJson(ret));
        	bw.flush();
        	bw.close();
        	bw=null;
        }catch(Exception e){
        	e.printStackTrace();
        }finally{
        	if(bw!=null){
        		try{bw.close();}catch(Exception e){}
        	}
        }
	}

	@SuppressWarnings("unchecked")
	private static List<Map> trainAPI(OkHttpClient client,Gson gson,String odpc){
		String url=URL_TOKYO_CH+odpc+"?acl:consumerKey="+KEY_TOKYO_CH;
		System.out.println(url);
        try{
    		Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .build();
            Response response = client.newCall(request).execute();
    		return gson.fromJson(response.body().string(), List.class);
        }catch(Exception e){
        	e.printStackTrace();
        	return null;
        }
	}

	static class Station{
		public String name_ja;
		public String name_en;
		public String sameAs;
		public List<String> lines=new ArrayList<String>();
	}
	static class RailLine{
		public String name_ja;
		public String name_en;
		public String sameAs;
		public String operator;
		public List<String> stations=new ArrayList<String>();
	}
}

2. Sortie JSON

L'exécution du code ci-dessus générera le fichier JSON suivant. En regardant cela, j'ai l'impression que ma compréhension du réseau ferroviaire dans la région métropolitaine de Tokyo s'est améliorée, en disant: "Il y a de telles lignes et il y a des gares comme celle-ci."

{
  "raillines": [
    {
      "name_ja": "Tramway Tokyo Sakura (ligne Toden Arakawa)",
      "name_en": "Tokyo Sakura Tram (Arakawa Line)",
      "sameAs": "odpt.Railway:Toei.Arakawa",
      "operator": "odpt.Operator:Toei",
      "stations": [
        "Pont de Minowa",
        "Arakawa Ichichu Mae",
        "Devant le bureau du quartier d'Arakawa",
        "Arakawa 2-chome",
        "Arakawa 7-chome",
        "Devant la gare de Machiya",
        "Machiya 2-chome",
        "Higashio Hisa 3-chome",
        "Devant Kumano",
        "Miyano-mae",
        "Petit stand",
        "Devant le parc d'attractions Arakawa",
        "Devant le garage Arakawa",
        "Kajiwara",
        "Sakaemachi",
        "Devant la gare d'Oji",
        "Asukayama",
        "Takinogawa 1-chome",
        "Nishigahara 4-chome",
        "Shinko Shinzuka",
        "Koshinzuka",
        "Negamo Nitta",
        "Devant la gare d'Otsuka",
        "Mukaihara",
        "Higashi Ikebukuro 4-chome",
        "Toden divers Shigaya",
        "Kishimojinmae",
        "Sous le centre d'études",
        "Pont d'Omokage",
        "Waseda"
      ]
    },
/*****réduction*******/
  "stations": {
    "Serada": {
      "name_ja": "Serada",
      "name_en": "Serada",
      "sameAs": "Serada",
      "lines": [
        "odpt.Railway:Tobu.Isesaki"
      ]
    },
    "Higashi Toshozawa": {
      "name_ja": "Higashi Toshozawa",
      "name_en": "Higashi-Tokorozawa",
      "sameAs": "Higashi Toshozawa",
      "lines": [
        "odpt.Railway:JR-East.Musashino"
      ]
    },
/*****réduction*******/

3. Afficher Force-Graph dans D3.js

J'ai chargé les informations de route / station dans D3.js et généré Force-Graph. J'ai créé Force-Graph avec D3.js pour la première fois, mais j'ai été surpris que ce soit beaucoup plus facile à faire que lors de l'écriture de GraphLayout avec java. D3.js est incroyable.

<!DOCTYPE html>
<html>
<head>
    <title>tokyo-challenge-test</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.2/d3.min.js"></script>
</head>
<body>
<svg></svg>
<script type="text/javascript">
    let width = 1200;
    let height = 800;
    const loadData = () => {
        d3.json("railway.json").then(function(json) {
            createMap(json);
        });
    };
    const createMap=(json)=>{
        const rail=json.raillines;
        const station=json.stations;
        let nodes=[];
        let links=[];
        let check={};
        let idv=0;
        for(let i=0;i<rail.length;i++){
            let sts=rail[i].stations;
            let tmp=[];
            for(let j=0;j<sts.length;j++){
                if(!check[sts[j]]){
                    let p={id:idv++,label:station[sts[j]].name_ja,val:1};
                    tmp.push(p);
                    nodes.push(p);
                    check[sts[j]]=p;
                }else{
                    check[sts[j]].val=check[sts[j]].val+1;
                    tmp.push(check[sts[j]]);
                }
            }
            for(let i=1;i<tmp.length;i++){
                let l={source:tmp[i-1].id,target:tmp[i].id};
                links.push(l);
            }
        }
        const svg = d3.select("svg").attr("width",width).attr("height",height);
        const link = d3.select("svg")
            .selectAll("line")
            .data(links)
            .enter()
            .append("line")
            .attr("stroke-width", 1)
            .attr("stroke", "#ccc");
        const node = d3.select("svg")
            .selectAll("g")
            .data(nodes)
            .enter()
            .append("circle")
            .attr("r",function(d){return d.val*5;})
            .attr("fill", "orange")
            .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));
        const label = d3.select("svg")
            .selectAll("g")
            .data(nodes)
            .enter()
            .append("text")
            .attr("text-anchor", "middle")
            .attr("dominant-baseline", "middle")
            .style("fill", "steelblue")
            .style("font-size", "9px")
            .text(function(d){return d.label;});
        const simulation = d3.forceSimulation()
            .force("link", d3.forceLink())
            .force("center", d3.forceCenter(600, 450))
            .force("charge", d3.forceManyBody().strength(-8))
            .force("x", d3.forceX().strength(0.05).x(width / 2))
            .force("y", d3.forceY().strength(0.05).y(height / 2));

        simulation.nodes(nodes).on("tick", ticked);
        simulation.force("link").links(links);
        function ticked() {
            link.attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; });
            node.attr("cx", function(d) { return d.x; })
                .attr("cy", function(d) { return d.y; });
            label.attr("x", function(d) { return d.x; })
                .attr("y", function(d) { return d.y; });
        }
        function dragstarted(d) {
            if(!d3.event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }
         function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }
        function dragended(d) {
            if(!d3.event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }
        const zoom = d3.zoom()
            .scaleExtent([1/4,4])
            .on('zoom', function(){
                node.attr("transform", d3.event.transform);
                link.attr("transform", d3.event.transform);
                label.attr("transform", d3.event.transform);
            });
        svg.call(zoom);
    }
    loadData();
</script>
</body>
</html>

finalement

Il y a trop de stations et d'itinéraires, donc le graphique n'est pas clair, mais il était surprenant qu'il y ait plus d'itinéraires à la gare de Shinjuku et à la gare de Shibuya qu'à la gare d'Ueno. Je voudrais concevoir un peu plus d'affichage et ajouter des données telles que la distance entre chaque station et le tarif à jouer.

Recommended Posts

Créons Force-Graph du réseau ferroviaire à partir des données ouvertes des transports publics de Tokyo
Faisons un pseudo modèle en utilisant active_hash ~ Prefectural data ~
Faisons un robot! "Une simple démo de Java AWT Robot"