1# -*- coding: utf-8 -*-
2#    This file is part of Gtfslib-python.
3#
4#    Gtfslib-python is free software: you can redistribute it and/or modify
5#    it under the terms of the GNU General Public License as published by
6#    the Free Software Foundation, either version 3 of the License, or
7#    (at your option) any later version.
8#
9#    Gtfslib-python is distributed in the hope that it will be useful,
10#    but WITHOUT ANY WARRANTY; without even the implied warranty of
11#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12#    GNU General Public License for more details.
13#
14#    You should have received a copy of the GNU General Public License
15#    along with gtfslib-python.  If not, see <http://www.gnu.org/licenses/>.
16"""
17@author: Laurent GRÉGOIRE <laurent.gregoire@mecatran.com>
18"""
19
20import unicodedata
21import shapefile
22from gtfslib.spatial import SpatialClusterizer
23from collections import defaultdict
24
25class ShapefileExport(object):
26    """
27    Export data (stops, hops) to ESRI shapefile.
28    Include frequency of use for each element
29    * number of trips
30    * number of trips x days
31    Note: Usually the trips x days is a better usage metric as
32    trips can be active on various number of days. If you want
33    to compute the average number of trips per day, divide the
34    result by the number of days (you can use date filtering).
35
36    Parameters:
37    --cluster=<dist>    Cluster stops closer than <dist> meters
38    --stopshp=<file>    Output stops to given shapefile
39    --hopshp=<file>     Output hops to given shapefile
40    """
41
42    def __init__(self):
43        pass
44
45    def remove_accents(self, strd):
46        nfkd_form = unicodedata.normalize('NFKD', strd)
47        only_ascii = nfkd_form.encode('ASCII', 'ignore')
48        return only_ascii
49
50    def run(self, context, stopshp=None, hopshp=None, cluster=0, **kwargs):
51        cluster_meters = float(cluster)
52        if stopshp is None and hopshp is None:
53            print("Nothing to generate! Bailing out")
54            return
55
56        print("Loading stops...")
57        stops = set()
58        sc = SpatialClusterizer(cluster_meters)
59        for stop in context.dao().stops(fltr=context.args.filter):
60            sc.add_point(stop)
61            stops.add(stop)
62        print("Loaded %d stops. Clusterize..." % (len(stops)))
63        sc.clusterize()
64        print("Aggregated in %d clusters" % (len(sc.clusters())))
65
66        print("Loading calendar dates")
67        dates = set(context.dao().calendar_dates_date(fltr=context.args.filter))
68        print("Loaded %d dates" % (len(dates)))
69
70        print("Computing stop and hop trip count...")
71        hop_tripcount = defaultdict(lambda: [0, 0])
72        clu_tripcount = defaultdict(lambda: [0, 0])
73        ntrips = 0
74        for trip in context.dao().trips(fltr=context.args.filter, prefetch_stop_times=True, prefetch_stops=True, prefetch_calendars=True):
75            # Compute the number of days the trip is running
76            # RESTRICTED ON THE FILTERED DATES
77            ndays = len([ date for date in trip.calendar.dates if date.as_date() in dates ])
78            for st1, st2 in trip.hops():
79                cluster1 = sc.cluster_of(st1.stop)
80                cluster2 = sc.cluster_of(st2.stop)
81                if cluster1 == cluster2:
82                    pass
83                key = (cluster1, cluster2)
84                hop_tripcount[key][0] += 1
85                hop_tripcount[key][1] += ndays
86                clu_tripcount[cluster1][0] += 1
87                clu_tripcount[cluster1][1] += ndays
88            ntrips += 1
89            if ntrips % 1000 == 0:
90                print("%d trips..." % ntrips)
91
92        if stopshp:
93            print("Generating stops cluster shapefile...")
94            stopshpwrt = shapefile.Writer(shapefile.POINT)
95            stopshpwrt.field("id", "N")
96            stopshpwrt.field("ids", "C", 100)
97            stopshpwrt.field("name", "C", 200)
98            stopshpwrt.field("ndep", "N")
99            stopshpwrt.field("ndepday", "N")
100            for cluster, (dep_count, depday_count) in clu_tripcount.items():
101                stopshpwrt.point(cluster.lon(), cluster.lat()) # X,Y ?
102                ids = cluster.aggregate(lambda s: s.stop_id, sep=';')
103                names = cluster.aggregate(lambda s: s.stop_name, sep=';')
104                stopshpwrt.record(cluster.id, self.remove_accents(ids),
105                                  self.remove_accents(names),
106                                  dep_count, depday_count)
107            stopshpwrt.save(stopshp)
108
109        if hopshp:
110            print("Generating hop shapefile...")
111            hopshpwrt = shapefile.Writer(shapefile.POLYLINE)
112            hopshpwrt.field("from_id", "N")
113            hopshpwrt.field("from_name", "C", 200)
114            hopshpwrt.field("to_id", "N")
115            hopshpwrt.field("to_name", "C", 200)
116            hopshpwrt.field("name", "C", 200)
117            hopshpwrt.field("ntrip", "N")
118            hopshpwrt.field("ntripday", "N")
119            for (c1, c2), (trip_count, tripday_count) in hop_tripcount.items():
120                c1name = c1.aggregate(lambda s: s.stop_name, sep=';')
121                c2name = c2.aggregate(lambda s: s.stop_name, sep=';')
122                hopshpwrt.line(parts=[[[c1.lon(), c1.lat()], [c2.lon(), c2.lat()]]])
123                hopshpwrt.record(c1.id, self.remove_accents(c1name), c2.id,
124                                 self.remove_accents(c2name),
125                                 self.remove_accents(c1name + " -> " + c2name),
126                                 trip_count, tripday_count)
127            hopshpwrt.save(hopshp)
128