1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    wxgui.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16import os
17import wx
18import numpy as np
19
20from agilepy.lib_wx.modulegui import ModuleGui
21from agilepy.lib_wx.ogleditor import *
22from agilepy.lib_base.processes import Process
23from agilepy.lib_wx.processdialog import ProcessDialog, ProcessDialogInteractive
24
25from coremodules.network import routing
26from coremodules.demand import demand
27
28import mapmatching
29
30
31#import results_mpl as results_mpl
32try:
33    import results_mpl as results_mpl
34    is_mpl = True  # we have matplotlib support
35except:
36    print "WARNING: python matplotlib package not installed, no matplotlib plots."
37    is_mpl = False
38
39
40class GpsPointsDrawings(Circles):
41    def __init__(self, ident, gpspoints, parent,   **kwargs):
42
43        Circles.__init__(self, ident,  parent, name='GPS points',
44                         is_parentobj=False,
45                         is_fill=True,  # Fill objects,
46                         is_outline=False,  # show outlines
47                         n_vert=11,  # default number of vertex per circle
48                         linewidth=3,
49                         **kwargs)
50
51        self.delete('centers')
52        self.delete('radii')
53
54        self.add(cm.AttrConf('color_default', np.array([1.0, 0.8, 0.1, 0.5], np.float32),
55                             groupnames=['options', 'colors'],
56                             metatype='color',
57                             perm='wr',
58                             name='Default color',
59                             info='Default point color.',
60                             ))
61
62        self.set_netelement(gpspoints)
63
64    def get_netelement(self):
65        return self._gpspoints
66
67    def get_centers_array(self):
68        # return self._gpspoints.coords.value[self._inds_map]
69        return self._gpspoints.coords[self.get_ids()]
70
71    def get_radii_array(self):
72        return self._gpspoints.radii[self.get_ids()]
73        # return self._gpspoints.radii.value[self._inds_map]
74
75    def is_tool_allowed(self, tool, id_drawobj=-1):
76        """
77        Returns True if this tool can be applied to this drawobj.
78        Optionally a particular drawobj can be specified with id_drawobj.
79        """
80        # basic tools:
81        return tool.ident not in ['select_handles', 'delete', 'move', 'stretch']  # 'configure',
82        # return tool.ident not in   ['delete','stretch']
83
84    def set_netelement(self, gpspoints):
85        # print 'set_nodes'
86        self._gpspoints = gpspoints
87        # if len(self)>0:
88        #    self.del_rows(self.get_ids())
89        self.clear_rows()
90
91        ids = self._gpspoints.get_ids_selected()
92        n = len(ids)
93
94        #self._inds_map = self._gpspoints.get_inds(ids)
95
96        # print 'color_node_default',self.color_node_default.value
97        # print 'colors\n',  np.ones((n,1),np.int32)*self.color_node_default.value
98        self.add_rows(ids=ids,
99                      #colors = np.ones((n,1),np.int32)*self.color_default.value,
100                      #colors_highl = self._get_colors_highl(np.ones((n,1),np.int32)*self.color_default.value),
101                      colors_fill=np.ones((n, 1), np.int32)*self.color_default.value,
102                      colors_highl_highl=self._get_colors_highl(np.ones((n, 1), np.int32)*self.color_default.value),
103                      #centers = self._nodes.coords[ids],
104                      #radii = self._nodes.radii[ids],
105                      )
106
107        self.update()
108
109    def update(self, is_update=True):
110
111        if is_update:
112            self._update_vertexvbo()
113            self._update_colorvbo()
114
115
116class GpsRoutesDrawings(Polylines):
117    def __init__(self, ident, edges, parent,   **kwargs):
118
119        # joinstyle
120        # FLATHEAD = 0
121        # BEVELHEAD = 1
122        Polylines.__init__(self, ident,  parent, name='GPS routes drawings',
123                           is_lefthalf=True,
124                           is_righthalf=True,
125                           arrowstretch=1.5,
126                           joinstyle=BEVELHEAD,  # FLATHEAD,#BEVELHEAD is good for both halfs,
127                           **kwargs)
128
129        # self.delete('vertices')
130        # self.delete('widths')
131        # self.delete('colors')
132
133        self.add(cm.AttrConf('width_default', 4.0,
134                             groupnames=['options'],
135                             perm='wr',
136                             name='Default width',
137                             info='Default route width of drawing.',
138                             ))
139
140        self.add(cm.AttrConf('color_default', np.array([1.0, 0.4, 0.0, 0.6], np.float32),
141                             groupnames=['options'],
142                             perm='wr',
143                             metatype='color',
144                             name='Default color',
145                             info='Default route color.',
146                             ))
147
148        self.set_netelement(edges)
149
150    def get_netelement(self):
151        return self._routes
152
153    # def get_vertices_array(self):
154    #    return self._routes.shapes[self.get_ids()]#.value[self._inds_map]#[self.get_ids()]
155
156    # def get_widths_array(self):
157    #    # double because only the right half is shown
158    #    # add a little bit to the width to make it a little wider than the lanes contained
159    #    #return 2.2*self._edges.widths.value[self._inds_map]
160    #    return 1.1*self._edges.widths[self.get_ids()]#.value[self._inds_map]
161
162    # def get_vertices(self, ids):
163    #    return self._edges.shapes[ids]
164
165    # def set_vertices(self, ids, vertices, is_update = True):
166    #    self._edges.set_shapes(ids, vertices)
167    #    if is_update:
168    #        self._update_vertexvbo()
169    #        self.parent.get_drawobj_by_ident('lanedraws').update()
170    #        self.parent.get_drawobj_by_ident('crossingsdraws').update()
171    #        self.parent.get_drawobj_by_ident('connectiondraws').update()
172
173    # def get_widths(self, ids):
174    #    return 1.1*self._edges.widths[ids]
175
176    # def set_widths(self, ids, values):
177    #    #self._edges.widths[ids] = values/1.1
178    #    pass
179
180    def is_tool_allowed(self, tool, id_drawobj=-1):
181        """
182        Returns True if this tool can be applied to this drawobj.
183        Optionally a particular drawobj can be specified with id_drawobj.
184        """
185        # basic tools:
186        return tool.ident not in ['configure', 'select_handles', 'delete', 'move', 'stretch']
187        # return tool.ident not in   ['delete',]
188
189    def set_netelement(self, routes):
190
191        self._routes = routes
192        #self._inds_edges = self._edges.get_inds()
193        self.clear_rows()
194        # if len(self)>0:
195        #    self.del_rows(self.get_ids())
196
197        ids = self._routes.parent.get_ids_route_selected()
198        #self._inds_map = self._edges.get_inds(ids)
199        n = len(ids)
200        #self.vertices = self._edges.shapes
201        #self.widths = self._edges.widths
202        # print '\n\nGpsRoutesDrawings.set_netelement',n
203        # print '  ids.dtype',ids.dtype
204        # print '  self._ids.dtype',self._ids.dtype
205        # print '  self._inds.dtype',self._inds.dtype
206        # print '  ids',ids
207        self.add_rows(ids=ids,
208                      beginstyles=np.ones(n, dtype=np.float32)*FLATHEAD,
209                      endstyles=np.ones(n, dtype=np.float32)*TRIANGLEHEAD,
210                      widths=np.ones(n, dtype=np.float32)*self.width_default.get_value()
211                      )
212        self.vertices[ids] = self._routes.get_shapes(ids)
213        self.update()
214
215    def update(self, is_update=True):
216        """
217        Update color, assume that there have not been structural changes of the arrays
218        """
219        # assumes that edges have been set in set_edges
220        # print 'Edgedrawing.update'
221        #edgeinds = self._edges.get_inds()
222        n = len(self)
223        ids = self.get_ids()
224
225        self.colors_fill.value[:] = self._routes.colors[ids]
226        #self.colors_fill.value[:] = np.ones((n,1),np.float32)*self.color_default.value
227        self.colors_fill_highl.value[:] = self._get_colors_highl(self.colors_fill.value)
228
229        if is_update:
230            self._update_vertexvbo()
231            self._update_colorvbo()
232
233
234class WxGui(ModuleGui):
235    """Contains functions that communicate between the widgets of the main wx gui
236    and the functions of the plugin.
237    """
238
239    def __init__(self, ident):
240        self._mapmatching = None
241        self._matchprocess = None
242        self._results = None
243        self._scenario = None
244        self._canvas = None
245        self._init_common(ident,  priority=100001,
246                          icondirpath=os.path.join(os.path.dirname(__file__), 'images'))
247
248        self._is_needs_refresh = False
249
250    def get_module(self):
251        return self._mapmatching
252
253    def get_scenario(self):
254        return self._mainframe.get_modulegui('coremodules.scenario').get_scenario()
255
256    def get_neteditor(self):
257        return self._mainframe.get_modulegui('coremodules.network').get_neteditor()
258
259    def get_canvas(self):
260        return self.get_neteditor().get_canvas()
261
262    def get_drawing(self):
263        return self.get_canvas().get_drawing()
264
265    def init_widgets(self, mainframe):
266        """
267        Set mainframe and initialize widgets to various places.
268        """
269        self._mainframe = mainframe
270        #self._neteditor = mainframe.add_view("Network", Neteditor)
271
272        # mainframe.browse_obj(self._module)
273        self.make_menu()
274        self.make_toolbar()
275
276    def refresh_widgets(self):
277        """
278        Check through mainframe what the state of the application is
279        and reset widgets. For exampe enable/disable widgets
280        dependent on the availability of data.
281        """
282        print 'MapmatchingWxGui.refresh_widgets'
283        scenario = self.get_scenario()
284        is_refresh = False
285        if self._scenario != scenario:
286            del self._scenario
287            del self._mapmatching
288            del self._results
289            self._scenario = scenario
290            self._mapmatching = scenario.demand.add_demandobject(
291                ident='mapmatching', DemandClass=mapmatching.Mapmatching)
292            #self._mapmatching =  mapmatching.Mapmatching('mapmatching', scenario)
293            self._matchprocess = None
294            self._results = mapmatching.Matchresults('matchresults',
295                                                     self._mapmatching,
296                                                     )
297            is_refresh = True
298
299        if is_refresh | self._is_needs_refresh:
300            self._is_needs_refresh = False
301            print '  is_refresh', is_refresh, self._is_needs_refresh
302            neteditor = self.get_neteditor()
303            #canvas = self.get_canvas()
304            drawing = self.get_drawing()  # canvas.get_drawing()
305
306            # add or refresh facility drawing
307            drawing.set_element('gpspointsdraws', GpsPointsDrawings,
308                                self._mapmatching.points, layer=150)
309
310            drawing.set_element('gpsroutesdraws', GpsRoutesDrawings,
311                                self._mapmatching.trips.get_routes(), layer=149)
312
313            # neteditor.get_toolbox().add_toolclass(AddZoneTool)# will check if tool is already there
314            # neteditor.get_toolbox().add_toolclass(AddFacilityTool)
315            neteditor.draw()
316
317        self._canvas = self.get_canvas()
318
319    def make_menu(self):
320        menubar = self._mainframe.menubar
321        menubar.append_menu('plugins/mapmatching',
322                            bitmap=self.get_icon("icon_gps.png"),
323                            )
324        menubar.append_item('plugins/mapmatching/browse',
325                            self.on_browse,  # common function in modulegui
326                            info='View and browse mapmatching in object panel.',
327                            bitmap=self.get_agileicon('icon_browse_24px.png'),  # ,
328                            )
329
330        # menubar.append_item( 'plugins/mapmatching/open...',
331        #    self.on_open,
332        #    bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,wx.ART_MENU),
333        #    )
334
335        menubar.append_menu('plugins/mapmatching/import',
336                            bitmap=self.get_agileicon("Document_Import_24px.png"),
337                            )
338
339        menubar.append_item('plugins/mapmatching/import/European cycling challange...',
340                            self.on_import_ecc,
341                            # info=self.on_import_ecc.__doc__.strip(),
342                            bitmap=self.get_agileicon("Document_Import_24px.png"),
343                            )
344
345        menubar.append_item('plugins/mapmatching/import/GPX file...',
346                            self.on_import_gpx,
347                            # info=self.on_import_ecc.__doc__.strip(),
348                            bitmap=self.get_agileicon("Document_Import_24px.png"),
349                            )
350
351        # menubar.append_item( 'plugins/mapmatching/project points',
352        #    self.on_project_points,
353        #    #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
354        #    )
355
356        menubar.append_item('plugins/mapmatching/match with birgillito method...',
357                            self.on_match_birgil,
358                            #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
359                            )
360
361        menubar.append_item('plugins/mapmatching/shortest path routing...',
362                            self.on_route_shortest,
363                            #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
364                            )
365
366        menubar.append_item('plugins/mapmatching/fastest path routing...',
367                            self.on_route_fastest,
368                            #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
369                            )
370
371
372# -------------------------------------------------------------------------------
373
374        menubar.append_menu('plugins/mapmatching/person analysis',
375                            bitmap=self.get_icon('icon_results_24px.png'),  # ,
376                            info='Person analysis tools'
377                            )
378
379        menubar.append_item('plugins/mapmatching/person analysis/analyze',
380                            self.on_analyze_persons,
381                            #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
382                            )
383
384        menubar.append_item('plugins/mapmatching/person analysis/save matched in csv...',
385                            self.on_export_persons_csv,
386                            info='Save persons with matched trips in a CSV file.',
387                            bitmap=self.get_agileicon("Document_Export_24px.png"),
388                            )
389
390# -------------------------------------------------------------------------------
391
392        menubar.append_menu('plugins/mapmatching/route analysis',
393                            bitmap=self.get_icon('icon_results_24px.png'),  # ,
394                            info='Route analysis tools'
395                            )
396
397        menubar.append_item('plugins/mapmatching/route analysis/browse',
398                            self.on_browse_results,  # common function in modulegui
399                            bitmap=self.get_agileicon('icon_browse_24px.png'),  # ,
400                            )
401
402        menubar.append_item('plugins/mapmatching/route analysis/analyze...',
403                            self.on_routeanalyze,  # common function in modulegui
404                            # bitmap = self.get_agileicon('icon_browse_24px.png'),#,
405                            )
406
407        if is_mpl:
408            menubar.append_item('plugins/mapmatching/route analysis/plot route results...',
409                                self.on_plot_routeresults,
410                                bitmap=results_mpl.get_mplicon(),  # ,
411                                )
412            menubar.append_item('plugins/mapmatching/route analysis/plot edge results...',
413                                self.on_plot_edgeresults,
414                                bitmap=results_mpl.get_mplicon(),  # ,
415                                )
416            menubar.append_item('plugins/mapmatching/route analysis/plot speed profiles...',
417                                self.on_plot_speedprofiles,
418                                bitmap=results_mpl.get_mplicon(),  # ,
419                                )
420            menubar.append_item('plugins/mapmatching/route analysis/plot node results...',
421                                self.on_plot_noderesults,
422                                bitmap=results_mpl.get_mplicon(),  # ,
423                                )
424
425        menubar.append_item('plugins/mapmatching/route analysis/safe as...',
426                            self.on_save_results,
427                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_MENU),
428                            )
429
430        menubar.append_item('plugins/mapmatching/route analysis/open...',
431                            self.on_open_results,
432                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_MENU),
433                            )
434
435        menubar.append_item('plugins/mapmatching/route analysis/route results to shape...',
436                            self.on_routes_to_shapefile,
437                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_MENU),
438                            )
439
440        menubar.append_item('plugins/mapmatching/route analysis/edge results to shape...',
441                            self.on_edgesresults_to_shapefile,
442                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_MENU),
443                            )
444
445        menubar.append_item('plugins/mapmatching/route analysis/GPS points to shape...',
446                            self.on_points_to_shapefile,
447                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_MENU),
448                            )
449
450# -------------------------------------------------------------------------------
451
452        menubar.append_menu('plugins/mapmatching/filter and select',
453                            # bitmap = self.get_icon('icon_results_24px.png'),#,
454                            info='Filter and select GPS trips.'
455                            )
456
457        menubar.append_item('plugins/mapmatching/filter and select/select traces by geometry...',
458                            self.on_geomfilter_trips,
459                            #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
460                            )
461
462        menubar.append_item('plugins/mapmatching/filter and select/filter trips...',
463                            self.on_postmatchfilter_trips,
464                            #bitmap = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS,wx.ART_MENU),
465                            )
466
467        menubar.append_item('plugins/mapmatching/filter and select/select all trips',
468                            self.on_select_all_trips,
469                            )
470
471        menubar.append_item('plugins/mapmatching/filter and select/unselect all trips',
472                            self.on_unselect_all_trips,
473                            )
474
475        menubar.append_item('plugins/mapmatching/filter and select/invert selected trips',
476                            self.on_invert_selected_trips,
477                            )
478# -------------------------------------------------------------------------------
479
480        menubar.append_menu('plugins/mapmatching/delete',
481                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_MENU),
482                            info='Delete tools.'
483                            )
484
485        menubar.append_item('plugins/mapmatching/delete/delete unselected trips',
486                            self.on_delete_unselected,
487                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_MENU),
488                            )
489
490        menubar.append_item('plugins/mapmatching/delete/delete routes',
491                            self.on_clear_routes,
492                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_MENU),
493                            )
494
495        menubar.append_item('plugins/mapmatching/delete/delete all',
496                            self.on_clear_all,
497                            bitmap=wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_MENU),
498                            )
499
500# -------------------------------------------------------------------------------
501
502        menubar.append_item('plugins/mapmatching/redraw GPS data',
503                            self.on_redraw,
504                            )
505
506    def on_plot_routeresults(self, event=None):
507        """
508        Plot route results of route analysis in Matplotlib plotting envitonment.
509        """
510        if is_mpl:
511            resultplotter = results_mpl.RouteresultPlotter(self._results,
512                                                           logger=self._mainframe.get_logger()
513                                                           )
514            dlg = results_mpl.ResultDialog(self._mainframe, resultplotter)
515
516            dlg.CenterOnScreen()
517
518            # this does not return until the dialog is closed.
519            val = dlg.ShowModal()
520            # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
521            # print '  status =',dlg.get_status()
522            if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
523                # print ">>>>>>>>>Unsuccessful\n"
524                dlg.Destroy()
525
526            if dlg.get_status() == 'success':
527                # print ">>>>>>>>>successful\n"
528                # apply current widget values to scenario instance
529                dlg.apply()
530                dlg.Destroy()
531
532    def on_plot_edgeresults(self, event=None):
533        """
534        Plot edge results of route analysis in Matplotlib plotting envitonment.
535        """
536        if is_mpl:
537            resultplotter = results_mpl.EdgeresultPlotter(self._results,
538                                                          logger=self._mainframe.get_logger()
539                                                          )
540            dlg = results_mpl.ResultDialog(self._mainframe, resultplotter)
541
542            dlg.CenterOnScreen()
543
544            # this does not return until the dialog is closed.
545            val = dlg.ShowModal()
546            # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
547            # print '  status =',dlg.get_status()
548            if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
549                # print ">>>>>>>>>Unsuccessful\n"
550                dlg.Destroy()
551
552            if dlg.get_status() == 'success':
553                # print ">>>>>>>>>successful\n"
554                # apply current widget values to scenario instance
555                dlg.apply()
556                dlg.Destroy()
557
558    def on_plot_noderesults(self, event=None):
559        """
560        Plot node results of route analysis in Matplotlib plotting envitonment.
561        """
562        if is_mpl:
563            resultplotter = results_mpl.NoderesultPlotter(self._results,
564                                                          logger=self._mainframe.get_logger()
565                                                          )
566            dlg = results_mpl.ResultDialog(self._mainframe, resultplotter)
567
568            dlg.CenterOnScreen()
569
570            # this does not return until the dialog is closed.
571            val = dlg.ShowModal()
572            # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
573            # print '  status =',dlg.get_status()
574            if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
575                # print ">>>>>>>>>Unsuccessful\n"
576                dlg.Destroy()
577
578            if dlg.get_status() == 'success':
579                # print ">>>>>>>>>successful\n"
580                # apply current widget values to scenario instance
581                dlg.apply()
582                dlg.Destroy()
583
584    def on_plot_speedprofiles(self, event=None):
585        """
586        Plot speedprofiles of route analysis in Matplotlib plotting envitonment.
587        """
588        if is_mpl:
589            resultplotter = results_mpl.SpeedprofilePlotter(self._results,
590                                                            logger=self._mainframe.get_logger()
591                                                            )
592            dlg = results_mpl.ResultDialog(self._mainframe, resultplotter)
593
594            dlg.CenterOnScreen()
595
596            # this does not return until the dialog is closed.
597            val = dlg.ShowModal()
598            # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
599            # print '  status =',dlg.get_status()
600            if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
601                # print ">>>>>>>>>Unsuccessful\n"
602                dlg.Destroy()
603
604            if dlg.get_status() == 'success':
605                # print ">>>>>>>>>successful\n"
606                # apply current widget values to scenario instance
607                dlg.apply()
608                dlg.Destroy()
609
610    def on_routes_to_shapefile(self, event=None):
611        """
612        Export route results to shape file.
613        """
614        # print 'on_routes_to_shapefile'
615        scenario = self._mapmatching.get_scenario()
616        dirpath = scenario.get_workdirpath()
617        defaultFile = scenario.get_rootfilename()+'.routeres.shp'
618        wildcards_all = 'All files (*.*)|*.*|SHP files (*.shp)|*.shp'
619        dlg = wx.FileDialog(None, message='Export route results to shapefile',
620                            defaultDir=dirpath, defaultFile=defaultFile,
621                            wildcard=wildcards_all, style=wx.SAVE | wx.CHANGE_DIR)
622        if dlg.ShowModal() == wx.ID_OK:
623            filepath = dlg.GetPath()
624
625        else:
626            return
627
628        mapmatching.routes_to_shapefile(self._mapmatching,
629                                        self._results,
630                                        filepath,
631                                        log=self._mainframe.get_logger())
632
633    def on_edgesresults_to_shapefile(self, event=None):
634        """
635        Export edge results to shape file.
636        """
637        print 'on_nodes_to_shapefile'
638        scenario = self._mapmatching.get_scenario()
639        dirpath = scenario.get_workdirpath()
640        defaultFile = scenario.get_rootfilename()+'.edgeres.shp'
641        wildcards_all = 'All files (*.*)|*.*|SHP files (*.shp)|*.shp'
642        dlg = wx.FileDialog(None, message='Export edge results to shapefile',
643                            defaultDir=dirpath, defaultFile=defaultFile,
644                            wildcard=wildcards_all, style=wx.SAVE | wx.CHANGE_DIR)
645        if dlg.ShowModal() == wx.ID_OK:
646            filepath = dlg.GetPath()
647
648        else:
649            return
650
651        mapmatching.edgesresults_to_shapefile(self._mapmatching,
652                                              self._results,
653                                              filepath,
654                                              log=self._mainframe.get_logger())
655
656    def on_points_to_shapefile(self, event=None):
657        """
658        Export GPS points to shapefile.
659        """
660        print 'on_points_to_shapefile'
661        scenario = self._mapmatching.get_scenario()
662        dirpath = scenario.get_workdirpath()
663        defaultFile = scenario.get_rootfilename()+'.points.shp'
664        wildcards_all = 'All files (*.*)|*.*|SHP files (*.shp)|*.shp'
665        dlg = wx.FileDialog(None, message='Export GPS points to shapefile',
666                            defaultDir=dirpath, defaultFile=defaultFile,
667                            wildcard=wildcards_all, style=wx.SAVE | wx.CHANGE_DIR)
668        if dlg.ShowModal() == wx.ID_OK:
669            filepath = dlg.GetPath()
670
671        else:
672            return
673
674        mapmatching.points_to_shapefile(self._mapmatching,
675                                        filepath,
676                                        log=self._mainframe.get_logger())
677
678    def on_save_results(self, event=None):
679        """
680        Save mapmatching analysis results to binary file.
681        """
682        if self._results is None:
683            return
684        scenario = self.get_scenario()
685        wildcards_all = "All files (*.*)|*.*"
686        wildcards_obj = "Python binary mapmatch files (*.mmatch.obj)|*.mmatch.obj|Python binary files (*.obj)|*.obj"
687        wildcards = wildcards_obj+"|"+wildcards_all
688
689        # Finally, if the directory is changed in the process of getting files, this
690        # dialog is set up to change the current working directory to the path chosen.
691        dlg = wx.FileDialog(
692            self._mainframe, message="Save results to file",
693            defaultDir=scenario.get_workdirpath(),
694            defaultFile=scenario.get_rootfilepath()+'.mmatch.obj',
695            wildcard=wildcards,
696            style=wx.SAVE | wx.CHANGE_DIR
697        )
698        val = dlg.ShowModal()
699        # Show the dialog and retrieve the user response. If it is the OK response,
700        # process the data.
701        if val == wx.ID_OK:
702            # This returns a Python list of files that were selected.
703            filepath = dlg.GetPath()
704            if len(filepath) > 0:
705                # now set new filename and workdir
706                self._results.save(filepath)
707
708        # Destroy the dialog. Don't do this until you are done with it!
709        # BAD things can happen otherwise!
710        dlg.Destroy()
711
712    def on_open_results(self, event=None):
713
714        wildcards_all = "All files (*.*)|*.*"
715        wildcards_obj = "Python binary mapmatch files (*.mmatch.obj)|*.mmatch.obj|Python binary files (*.obj)|*.obj"
716        wildcards = wildcards_obj+"|"+wildcards_all
717
718        # Finally, if the directory is changed in the process of getting files, this
719        # dialog is set up to change the current working directory to the path chosen.
720        dlg = wx.FileDialog(
721            self._mainframe, message="Open results file",
722            defaultDir=self.get_scenario().get_workdirpath(),
723            #defaultFile = os.path.join(scenario.get_workdirpath(), scenario.format_ident()+'.obj'),
724            wildcard=wildcards,
725            style=wx.OPEN | wx.CHANGE_DIR
726        )
727
728        # Show the dialog and retrieve the user response. If it is the OK response,
729        # process the data.
730        is_newresults = False
731        if dlg.ShowModal() == wx.ID_OK:
732            # This returns a Python list of files that were selected.
733            filepath = dlg.GetPath()
734            if len(filepath) > 0:
735                if self._results is not None:
736                    # browse away from results
737                    # self._mainframe.browse_obj(self._results.get_scenario())
738                    del self._results
739
740                self._results = mapmatching.load_results(filepath,
741                                                         parent=self._mapmatching,
742                                                         logger=self._mainframe.get_logger()
743                                                         )
744                is_newresults = True
745
746        # Destroy the dialog. Don't do this until you are done with it!
747        # BAD things can happen otherwise!
748        dlg.Destroy()
749
750        if is_newresults:
751            # this should update all widgets for the new scenario!!
752            # print 'call self._mainframe.refresh_moduleguis()'
753            self._mainframe.browse_obj(self._results)
754            # self._mainframe.select_view(name = "Result viewer") #!!!!!!!!tricky, crashes without
755            self._is_needs_refresh = True
756            self.refresh_widgets()
757            # wx.CallAfter(self.refresh_widgets)
758            # self._mainframe.refresh_moduleguis()
759            #if event: event.Skip()
760
761    def on_select_all_trips(self, event=None):
762        """
763        Select all GPS trips.
764        """
765        self._mapmatching.trips.select_all()
766        self._mainframe.browse_obj(self._mapmatching.trips)
767        self._is_needs_refresh = True
768        self.refresh_widgets()
769
770    def on_unselect_all_trips(self, event=None):
771        """
772        Unselect all GPS trips.
773        """
774        self._mapmatching.trips.unselect_all()
775        self._mainframe.browse_obj(self._mapmatching.trips)
776        self._is_needs_refresh = True
777        self.refresh_widgets()
778
779    def on_invert_selected_trips(self, event=None):
780        """
781        Invert selected GPS trips, all selected will be unselected and vice versa.
782        """
783        self._mapmatching.trips.invert_selection()
784        self._mainframe.browse_obj(self._mapmatching.trips)
785        self._is_needs_refresh = True
786        self.refresh_widgets()
787
788    def on_clear_all(self, event=None):
789        """
790        Clear all GPS points, routes and persons.
791        """
792        self._mapmatching.clear_all()
793        self._mainframe.browse_obj(self._mapmatching)
794        self._is_needs_refresh = True
795        self.refresh_widgets()
796
797    def on_clear_routes(self, event=None):
798        """
799        Clear matched routes and minimal distance routes.
800        """
801        self._mapmatching.clear_routes()
802        self._mainframe.browse_obj(self._mapmatching)
803        self._is_needs_refresh = True
804        self.refresh_widgets()
805
806    def on_delete_unselected(self, event=None):
807        """
808        Delete unselected trips.
809        """
810        self._mapmatching.delete_unselected_trips()
811        self._mainframe.browse_obj(self._mapmatching.trips)
812        #self._is_needs_refresh = True
813        # self.refresh_widgets()
814
815    def is_matchprocess(self, ident):
816        if self._matchprocess is None:
817            return False
818        else:
819            return self._matchprocess.ident == ident
820
821    def on_match_birgil(self, event=None):
822        """
823        Match selected traces with Birgillito's method.
824        """
825        # self.prepare_results()
826        if not self.is_matchprocess('birgilmatcher'):
827            self._matchprocess = mapmatching.BirgilMatcher('birgilmatcher',
828                                                           self._mapmatching,
829                                                           logger=self._mainframe.get_logger(),
830                                                           )
831
832        dlg = ProcessDialogInteractive(self._mainframe,
833                                       self._matchprocess,
834                                       #title = 'Mapmatching with Birgillito method',
835                                       func_close=self.close_match_birgil,
836                                       )
837
838        dlg.CenterOnScreen()
839
840        # this does not return until the dialog is closed.
841        #val = dlg.ShowModal()
842        # print 'open_sumodialog_interactive'
843        dlg.Show()
844        dlg.MakeModal(True)
845        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
846        # print '  status =',dlg.get_status()
847        # print 'returned to main window self.simulator.status',self.simulator.status
848
849    def close_match_birgil(self, dlg):
850        # called before destroying the dialog
851        if dlg.get_status() == 'success':
852            #p = self._mapmatchprocess
853
854            self._mainframe.browse_obj(self._mapmatching.trips)
855            self._is_needs_refresh = True
856            self.refresh_widgets()
857
858    def on_geomfilter_trips(self, event=None):
859        """
860        Select GPS traces to satisfy geometric requirements.
861        This should be done before the mapmatching process.
862        """
863        p = mapmatching.TripGeomfilter(self._mapmatching, logger=self._mainframe.get_logger())
864        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
865
866        dlg.CenterOnScreen()
867
868        # this does not return until the dialog is closed.
869        val = dlg.ShowModal()
870        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
871        # print '  status =',dlg.get_status()
872        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
873            # print ">>>>>>>>>Unsuccessful\n"
874            dlg.Destroy()
875
876        if dlg.get_status() == 'success':
877            # print ">>>>>>>>>successful\n"
878            # apply current widget values to scenario instance
879            dlg.apply()
880            dlg.Destroy()
881            self._mainframe.browse_obj(self._mapmatching.trips)
882            self._is_needs_refresh = True
883            self.refresh_widgets()
884
885    def on_postmatchfilter_trips(self, event=None):
886        """
887        Select trips by different parameters to ensure the quality of the mapmatching results.
888        This should be done after the map-matching process.
889        """
890        p = mapmatching.PostMatchfilter(self._mapmatching, logger=self._mainframe.get_logger())
891        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
892
893        dlg.CenterOnScreen()
894
895        # this does not return until the dialog is closed.
896        val = dlg.ShowModal()
897        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
898        # print '  status =',dlg.get_status()
899        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
900            # print ">>>>>>>>>Unsuccessful\n"
901            dlg.Destroy()
902
903        if dlg.get_status() == 'success':
904            # print ">>>>>>>>>successful\n"
905            # apply current widget values to scenario instance
906            dlg.apply()
907            dlg.Destroy()
908            self._mainframe.browse_obj(self._mapmatching.trips)
909            self._is_needs_refresh = True
910            self.refresh_widgets()
911
912    def on_route_shortest(self, event=None):
913        """
914        Shortest path routing of matched routes.
915        """
916        p = mapmatching.Shortestrouter('shortestpathrouter', self._mapmatching, logger=self._mainframe.get_logger())
917        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
918
919        dlg.CenterOnScreen()
920
921        # this does not return until the dialog is closed.
922        val = dlg.ShowModal()
923        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
924        # print '  status =',dlg.get_status()
925        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
926            # print ">>>>>>>>>Unsuccessful\n"
927            dlg.Destroy()
928
929        if dlg.get_status() == 'success':
930            # print ">>>>>>>>>successful\n"
931            # apply current widget values to scenario instance
932            dlg.apply()
933            dlg.Destroy()
934            self._mainframe.browse_obj(self._mapmatching.trips)
935            self._is_needs_refresh = True
936            self.refresh_widgets()
937
938    def on_route_fastest(self, event=None):
939        """
940        Fastest path routing of matched routes.
941        """
942        p = mapmatching.Fastestrouter('fastestpathrouter', self._mapmatching,
943                                      matchresults=self._results,
944                                      logger=self._mainframe.get_logger()
945                                      )
946        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
947
948        dlg.CenterOnScreen()
949
950        # this does not return until the dialog is closed.
951        val = dlg.ShowModal()
952        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
953        # print '  status =',dlg.get_status()
954        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
955            # print ">>>>>>>>>Unsuccessful\n"
956            dlg.Destroy()
957
958        if dlg.get_status() == 'success':
959            # print ">>>>>>>>>successful\n"
960            # apply current widget values to scenario instance
961            dlg.apply()
962            dlg.Destroy()
963            self._mainframe.browse_obj(self._mapmatching.trips)
964            self._is_needs_refresh = True
965            self.refresh_widgets()
966
967    def on_redraw(self, event=None):
968        self._mainframe.browse_obj(self._mapmatching)
969        self._is_needs_refresh = True
970        self.refresh_widgets()
971
972    def on_import_ecc(self, event=None):
973        """
974        Import and filter data from a European cycling challange.
975        """
976        p = mapmatching.EccTracesImporter(self._mapmatching, logger=self._mainframe.get_logger())
977        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
978
979        dlg.CenterOnScreen()
980
981        # this does not return until the dialog is closed.
982        val = dlg.ShowModal()
983        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
984        # print '  status =',dlg.get_status()
985        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
986            # print ">>>>>>>>>Unsuccessful\n"
987            dlg.Destroy()
988
989        if dlg.get_status() == 'success':
990            # print ">>>>>>>>>successful\n"
991            # apply current widget values to scenario instance
992            dlg.apply()
993            dlg.Destroy()
994            self._mainframe.browse_obj(self._mapmatching.trips)
995            self._is_needs_refresh = True
996            self.refresh_widgets()
997
998    def on_import_gpx(self, event=None):
999        """
1000        Import and filter data from GPX file.
1001        """
1002        p = mapmatching.GpxImporter(self._mapmatching, logger=self._mainframe.get_logger())
1003        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
1004
1005        dlg.CenterOnScreen()
1006
1007        # this does not return until the dialog is closed.
1008        val = dlg.ShowModal()
1009        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
1010        # print '  status =',dlg.get_status()
1011        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
1012            # print ">>>>>>>>>Unsuccessful\n"
1013            dlg.Destroy()
1014
1015        if dlg.get_status() == 'success':
1016            # print ">>>>>>>>>successful\n"
1017            # apply current widget values to scenario instance
1018            dlg.apply()
1019            dlg.Destroy()
1020            self._mainframe.browse_obj(self._mapmatching.trips)
1021            self._is_needs_refresh = True
1022            self.refresh_widgets()
1023
1024    def on_project_points(self, event=None):
1025        self._mapmatching.points.project()
1026        self._mainframe.browse_obj(self._mapmatching.points)
1027
1028        if event:
1029            event.Skip()
1030
1031    def on_browse(self, event=None):
1032
1033        self._mainframe.browse_obj(self._mapmatching)
1034        if event:
1035            event.Skip()
1036
1037    def on_browse_results(self, event=None):
1038        """
1039        Browse mapmatching analyses results
1040        """
1041        self._mainframe.browse_obj(self._results)
1042        if event:
1043            event.Skip()
1044
1045    def on_analyze_persons(self, event=None):
1046        """
1047        Analyze the trips of each person in the database.
1048        Ensure that mapmatching and shortest trip routing
1049        has been previously executed.
1050        """
1051
1052        self._mapmatching.persons.analyze()
1053        self._mainframe.browse_obj(self._mapmatching.persons)
1054
1055    def on_export_persons_csv(self, event=None):
1056        if self._results is None:
1057            return
1058        scenario = self._results.get_scenario()
1059        wildcards_all = "All files (*.*)|*.*"
1060        wildcards_obj = "CSV files (*.csv)|*.csv|Text file (*.txt)|*.txt"
1061        wildcards = wildcards_obj+"|"+wildcards_all
1062
1063        # Finally, if the directory is changed in the process of getting files, this
1064        # dialog is set up to change the current working directory to the path chosen.
1065        dlg = wx.FileDialog(
1066            self._mainframe, message="Export persons to CSV file",
1067            defaultDir=scenario.get_workdirpath(),
1068            defaultFile=scenario.get_rootfilepath()+'.gpspersons.csv',
1069            wildcard=wildcards,
1070            style=wx.SAVE | wx.CHANGE_DIR
1071        )
1072        val = dlg.ShowModal()
1073        # Show the dialog and retrieve the user response. If it is the OK response,
1074        # process the data.
1075        if val == wx.ID_OK:
1076            # This returns a Python list of files that were selected.
1077            filepath = dlg.GetPath()
1078            if len(filepath) > 0:
1079                # now set new filename and workdir
1080                persons = self._mapmatching.persons
1081                ids_pers = persons.select_ids(persons.lengths_tot_route_matched.get_value() > 0)
1082                self._mapmatching.persons.export_csv(filepath, ids=ids_pers)
1083
1084        # Destroy the dialog. Don't do this until you are done with it!
1085        # BAD things can happen otherwise!
1086        dlg.Destroy()
1087
1088    def on_routeanalyze(self, event=None):
1089        """
1090        Analyze attributes of matched and alternative routes.
1091        """
1092        p = mapmatching.Routesanalyzer('routeanalyzer',
1093                                       self._mapmatching,
1094                                       self._results,
1095                                       logger=self._mainframe.get_logger())
1096
1097        dlg = ProcessDialog(self._mainframe, p, immediate_apply=True)
1098
1099        dlg.CenterOnScreen()
1100
1101        # this does not return until the dialog is closed.
1102        val = dlg.ShowModal()
1103        # print '  val,val == wx.ID_OK',val,wx.ID_OK,wx.ID_CANCEL,val == wx.ID_CANCEL
1104        # print '  status =',dlg.get_status()
1105        if dlg.get_status() != 'success':  # val == wx.ID_CANCEL:
1106            # print ">>>>>>>>>Unsuccessful\n"
1107            dlg.Destroy()
1108
1109        if dlg.get_status() == 'success':
1110            # print ">>>>>>>>>successful\n"
1111            # apply current widget values to scenario instance
1112            dlg.apply()
1113            dlg.Destroy()
1114            self._mainframe.browse_obj(self._results)
1115            self._is_needs_refresh = True
1116            self.refresh_widgets()
1117