Browse Source

change to OSM Tools and new ORS version

OSMtools_IncludeGraphHopper
Nils 2 years ago
parent
commit
788fe76578
13 changed files with 2431 additions and 18 deletions
  1. 5
    5
      Makefile
  2. 0
    2
      README.md
  3. 4
    4
      __init__.py
  4. 3
    3
      metadata.txt
  5. 249
    0
      osm_tools.py
  6. 317
    0
      osm_tools_access.py
  7. 41
    0
      osm_tools_dialog.py
  8. 1319
    0
      osm_tools_dialog_base.ui
  9. 64
    0
      osm_tools_geocode.py
  10. 70
    0
      osm_tools_pointtool.py
  11. 355
    0
      osm_tools_routing.py
  12. 3
    3
      pb_tool.cfg
  13. 1
    1
      resources_rc.py

+ 5
- 5
Makefile View File

@@ -1,5 +1,5 @@
1 1
 #/***************************************************************************
2
-# ORStools
2
+# OSMtools
3 3
 #
4 4
 # falk
5 5
 #							 -------------------
@@ -38,15 +38,15 @@ LOCALES =
38 38
 # translation
39 39
 SOURCES = \
40 40
 	__init__.py \
41
-	ors_tools.py ors_tools_dialog.py
41
+	osm_tools.py osm_tools_dialog.py
42 42
 
43
-PLUGINNAME = ORStools
43
+PLUGINNAME = OSMtools
44 44
 
45 45
 PY_FILES = \
46 46
 	__init__.py \
47
-	ors_tools.py ors_tools_dialog.py
47
+	osm_tools.py osm_tools_dialog.py
48 48
 
49
-UI_FILES = ors_tools_dialog_base.ui
49
+UI_FILES = osm_tools_dialog_base.ui
50 50
 
51 51
 EXTRAS = metadata.txt icon.png
52 52
 

+ 0
- 2
README.md View File

@@ -4,8 +4,6 @@ Set of tools to use OpenRouteService (ORS) API´s as a plugin in QGIS (www.openr
4 4
 # Functionalities
5 5
 Use QGIS to generate input for routing and accessibility area analysis powered by ORS, either via clicking coordinates in the map canvas or using point layers for batch operation. 
6 6
 
7
-Coordinates of both (clicking tool and point layers) will be transformed to EPSG:4326 internally and sent to ORS API. Output files are also in EPSG:4326.
8
-
9 7
 By default, a public API key is used which is limited to 2500 requests/day.
10 8
 
11 9
 # Disclaimer

+ 4
- 4
__init__.py View File

@@ -1,7 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 """
3 3
 /***************************************************************************
4
- ORStools
4
+ OSMtools
5 5
                                  A QGIS plugin
6 6
  falk
7 7
                              -------------------
@@ -25,11 +25,11 @@
25 25
 
26 26
 # noinspection PyPep8Naming
27 27
 def classFactory(iface):  # pylint: disable=invalid-name
28
-    """Load ORStools class from file ORStools.
28
+    """Load OSMtools class from file OS;tools.
29 29
 
30 30
     :param iface: A QGIS interface instance.
31 31
     :type iface: QgsInterface
32 32
     """
33 33
     #
34
-    from .ors_tools import ORStools
35
-    return ORStools(iface)
34
+    from .osm_tools import OSMtools
35
+    return OSMtools(iface)

+ 3
- 3
metadata.txt View File

@@ -7,9 +7,9 @@
7 7
 # This file should be included when you package your plugin.# Mandatory items:
8 8
 
9 9
 [general]
10
-name=ORS Tools
10
+name=OSM Tools
11 11
 qgisMinimumVersion=2.0
12
-description=ORS Tools provides access to the routing functions of OpenRouteService.org, based on OSM. The tool set includes basic routing and calculation of accessibility areas, both either interactive or from QGIS point files.
12
+description=OSM Tools provides access to the most of the functions of OpenRouteService.org, based on OpenStreetMap. The tool set includes basic routing and calculation of accessibility areas, both either interactive or from QGIS point files.
13 13
 version=0.1
14 14
 author=Nils Nolde
15 15
 email=nilsnolde@gmail.com
@@ -26,7 +26,7 @@ repository=https://github.com/nilsnolde/ORStools.git
26 26
 changelog=2017/02/21 v.0.1 beta version
27 27
 
28 28
 # Tags are comma separated with spaces allowed
29
-tags=routing, OSM, openstreetmap, openrouteservice, service area, accessibility area
29
+tags=routing, OSM, openstreetmap, openrouteservice, service area, accessibility area, route
30 30
 
31 31
 homepage=https://github.com/nilsnolde/ORStools
32 32
 category=Plugins

+ 249
- 0
osm_tools.py View File

@@ -0,0 +1,249 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+/***************************************************************************
4
+ OSMtools
5
+                                 A QGIS plugin
6
+ falk
7
+                              -------------------
8
+        begin                : 2017-02-01
9
+        git sha              : $Format:%H$
10
+        copyright            : (C) 2017 by Nils Nolde
11
+        email                : nils.nolde@gmail.com
12
+ ***************************************************************************/
13
+ 
14
+ This plugin provides access to the various APIs from OpenRouteService 
15
+ (http://openrouteservice.readthedocs.io/en/1.0/api.html), developed and 
16
+ maintained by GIScience team at University of Heidelberg, Germany. By using 
17
+ this plugin you agree to the ORS terms of service
18
+ (http://openrouteservice.readthedocs.io/en/1.0/tos.html#terms-of-service).
19
+ 
20
+/***************************************************************************
21
+ *                                                                         *
22
+ *   This program is free software; you can redistribute it and/or modify  *
23
+ *   it under the terms of the GNU General Public License as published by  *
24
+ *   the Free Software Foundation; either version 2 of the License, or     *
25
+ *   (at your option) any later version.                                   *
26
+ *                                                                         *
27
+ ***************************************************************************/
28
+"""
29
+from PyQt4.QtCore import *
30
+from PyQt4.QtGui import *
31
+# Initialize Qt resources from file resources.py
32
+import resources_rc
33
+# Import the code for the dialog
34
+from osm_tools_dialog import OSMtoolsDialog
35
+import os.path
36
+
37
+import osm_tools_access
38
+import osm_tools_routing
39
+
40
+from qgis.core import *
41
+import qgis.gui
42
+import qgis.utils
43
+
44
+import logging
45
+
46
+logging.basicConfig(format='%(levelname)s:%(message)s', level = logging.INFO)
47
+
48
+class OSMtools:
49
+    """QGIS Plugin Implementation."""
50
+
51
+    def __init__(self, iface):
52
+        """Constructor.
53
+
54
+        :param iface: An interface instance that will be passed to this class
55
+            which provides the hook by which you can manipulate the QGIS
56
+            application at run time.
57
+        :type iface: QgsInterface
58
+        """
59
+        # Save reference to the QGIS interface
60
+        self.iface = iface
61
+        # initialize plugin directory
62
+        self.plugin_dir = os.path.dirname(__file__)
63
+        # initialize locale
64
+        locale = QSettings().value('locale/userLocale')[0:2]
65
+        locale_path = os.path.join(
66
+            self.plugin_dir,
67
+            'i18n',
68
+            'OSMtools_{}.qm'.format(locale))
69
+
70
+        if os.path.exists(locale_path):
71
+            self.translator = QTranslator()
72
+            self.translator.load(locale_path)
73
+
74
+            if qVersion() > '4.3.3':
75
+                QCoreApplication.installTranslator(self.translator)
76
+
77
+        # Declare instance attributes
78
+        self.actions = []
79
+        self.menu = self.tr(u'&OSM Tools')
80
+        # TODO: We are going to let the user set this up in a future iteration
81
+        self.toolbar = self.iface.addToolBar(u'OSMtools')
82
+        self.toolbar.setObjectName(u'OSMtools')
83
+        
84
+        #custom __init__ declarations
85
+        self.dlg = OSMtoolsDialog()
86
+        
87
+        self.canvas = qgis.utils.iface.mapCanvas()
88
+        self.script_dir = os.path.dirname(os.path.abspath(__file__))
89
+        
90
+    # noinspection PyMethodMayBeStatic
91
+    def tr(self, message):
92
+        """Get the translation for a string using Qt translation API.
93
+
94
+        We implement this ourselves since we do not inherit QObject.
95
+
96
+        :param message: String for translation.
97
+        :type message: str, QString
98
+
99
+        :returns: Translated version of message.
100
+        :rtype: QString
101
+        """
102
+        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
103
+        return QCoreApplication.translate('OSMtools', message)
104
+
105
+
106
+    def add_action(
107
+        self,
108
+        icon_path,
109
+        text,
110
+        callback,
111
+        enabled_flag=True,
112
+        add_to_menu=True,
113
+        add_to_toolbar=True,
114
+        status_tip=None,
115
+        whats_this=None,
116
+        parent=None):
117
+        """Add a toolbar icon to the toolbar.
118
+
119
+        :param icon_path: Path to the icon for this action. Can be a resource
120
+            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
121
+        :type icon_path: str
122
+
123
+        :param text: Text that should be shown in menu items for this action.
124
+        :type text: str
125
+
126
+        :param callback: Function to be called when the action is triggered.
127
+        :type callback: function
128
+
129
+        :param enabled_flag: A flag indicating if the action should be enabled
130
+            by default. Defaults to True.
131
+        :type enabled_flag: bool
132
+
133
+        :param add_to_menu: Flag indicating whether the action should also
134
+            be added to the menu. Defaults to True.
135
+        :type add_to_menu: bool
136
+
137
+        :param add_to_toolbar: Flag indicating whether the action should also
138
+            be added to the toolbar. Defaults to True.
139
+        :type add_to_toolbar: bool
140
+
141
+        :param status_tip: Optional text to show in a popup when mouse pointer
142
+            hovers over the action.
143
+        :type status_tip: str
144
+
145
+        :param parent: Parent widget for the new action. Defaults None.
146
+        :type parent: QWidget
147
+
148
+        :param whats_this: Optional text to show in the status bar when the
149
+            mouse pointer hovers over the action.
150
+
151
+        :returns: The action that was created. Note that the action is also
152
+            added to self.actions list.
153
+        :rtype: QAction
154
+        """
155
+
156
+        # Create the dialog (after translation) and keep reference
157
+        
158
+
159
+        icon = QIcon(icon_path)
160
+        action = QAction(icon, text, parent)
161
+        action.triggered.connect(callback)
162
+        action.setEnabled(enabled_flag)
163
+
164
+        if status_tip is not None:
165
+            action.setStatusTip(status_tip)
166
+
167
+        if whats_this is not None:
168
+            action.setWhatsThis(whats_this)
169
+
170
+        if add_to_toolbar:
171
+            self.toolbar.addAction(action)
172
+
173
+        if add_to_menu:
174
+            self.iface.addPluginToMenu(
175
+                self.menu,
176
+                action)
177
+
178
+        self.actions.append(action)
179
+            
180
+        return action
181
+    
182
+    def initGui(self):
183
+        """Create the menu entries and toolbar icons inside the QGIS GUI."""
184
+
185
+        icon_path = ':/plugins/OSMtools/icon.png'
186
+        self.add_action(
187
+            icon_path,
188
+            text=self.tr(u'OSM Tools'),
189
+            callback=self.run,
190
+            parent=self.iface.mainWindow())
191
+        
192
+        self.dlg.api_key.textChanged.connect(self.keyWriter)
193
+        
194
+        self.dlg.key_order.setText("<a href = 'mailto:openrouteservice@geog.uni-heidelberg.de?subject=OSM API key request'>Get Key!</a>") 
195
+        self.dlg.key_order.connect(self.dlg.key_order, SIGNAL("linkActivated(QString)"), self.OpenURL) 
196
+        self.dlg.header_2.linkActivated.connect(self.OpenURL)
197
+        self.dlg.header_3.linkActivated.connect(self.OpenURL)
198
+        
199
+        
200
+    def OpenURL(self, URL): 
201
+          QDesktopServices().openUrl(QUrl(URL))
202
+                
203
+    
204
+    def unload(self):        
205
+        self.dlg.close()
206
+        QApplication.restoreOverrideCursor()
207
+        
208
+        
209
+    def run(self):
210
+        """Run method that performs all the real work"""
211
+        
212
+        # Populate the api key lineEdit widget
213
+        with open(os.path.join(self.script_dir, "apikey.txt")) as key:
214
+            self.dlg.api_key.setText(key.read())
215
+        
216
+        # Initiate analysis classes
217
+        self.access_anal = osm_tools_access.accessAnalysis(self.dlg)
218
+        self.route_anal = osm_tools_routing.routing(self.dlg)
219
+        
220
+        self.dlg.setFixedSize(self.dlg.size())  
221
+        
222
+        self.dlg.show()
223
+        
224
+        # show the dialog
225
+        # Run the dialog event loop
226
+        result = self.dlg.exec_()
227
+        # See if OK was pressed
228
+        if result:
229
+            if self.dlg.tabWidget.currentIndex() == 1 and self.dlg.use_layer.isChecked():
230
+                self.access_anal.iterAnalysis()
231
+            
232
+            elif self.dlg.tabWidget.currentIndex() == 0:
233
+                self.route_anal.route()
234
+        else:
235
+            self.unload()
236
+                
237
+    def keyWriter(self):
238
+        with open(os.path.join(self.script_dir, "apikey.txt"), 'w') as key:
239
+            return key.write(self.dlg.api_key.text())
240
+        
241
+        
242
+def CheckCRS(self,crs):
243
+    check = True
244
+    if crs != "EPSG:4326":
245
+        msg = "CRS is {}. Must be EPSG:4326 (WGS84)".format(crs)
246
+        qgis.utils.iface.messageBar().pushMessage(msg, level = qgis.gui.QgsMessageBar.CRITICAL)
247
+        check = False
248
+    return check
249
+    

+ 317
- 0
osm_tools_access.py View File

@@ -0,0 +1,317 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Created on Mon Feb 06 15:26:47 2017
4
+
5
+@author: nnolde
6
+"""
7
+from PyQt4.QtCore import *
8
+from PyQt4.QtGui import *
9
+
10
+from qgis.core import *
11
+from qgis.gui import * 
12
+import qgis.utils
13
+import processing
14
+
15
+import requests
16
+import xml.etree.ElementTree as ET
17
+import os.path
18
+
19
+import osm_tools       
20
+import osm_tools_geocode
21
+import osm_tools_pointtool
22
+        
23
+class accessAnalysis:
24
+    def __init__(self, dlg):
25
+        self.dlg = dlg
26
+        self.url = r"http://openls.geog.uni-heidelberg.de/analyse?"
27
+        self.ns = {'gml': 'http://www.opengis.net/gml',
28
+                  'aas': "http://www.geoinform.fh-mainz.de/aas",
29
+                  'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
30
+                  
31
+        self.mapTool = None
32
+
33
+        self.dlg.mode.clear()
34
+        self.dlg.layer.clear()
35
+        self.dlg.method.clear()  
36
+        self.dlg.mode.addItem('Car')
37
+        self.dlg.mode.addItem('Bicycle')
38
+        self.dlg.mode.addItem('Pedestrian')      
39
+        self.dlg.method.addItem('RecursiveGrid')
40
+        self.dlg.method.addItem('TIN') 
41
+        
42
+        for layer in qgis.utils.iface.legendInterface().layers():
43
+            layerType = layer.type()
44
+            if layerType == QgsMapLayer.VectorLayer and layer.wkbType() == QGis.WKBPoint:
45
+                self.dlg.layer.addItem(layer.name())
46
+                  
47
+        # GUI Init
48
+        self.popBox()
49
+        
50
+        # API parameters
51
+        self.api_key = self.dlg.api_key.text()
52
+        self.iso_max = self.dlg.iso_max.value()
53
+        self.iso_int = self.dlg.iso_int.value() * 60
54
+        self.iso_mode = self.dlg.mode.currentText()
55
+        self.iso_method = self.dlg.method.currentText()
56
+        self.iface = qgis.utils.iface
57
+        
58
+        self.script_dir = os.path.dirname(os.path.abspath(__file__))
59
+        
60
+        # Connect events to signals
61
+        self.dlg.iso_max.valueChanged.connect(self.valueChanged)
62
+        self.dlg.iso_int.valueChanged.connect(self.valueChanged)
63
+        self.dlg.mode.currentIndexChanged.connect(self.valueChanged)
64
+        self.dlg.method.currentIndexChanged.connect(self.valueChanged)
65
+        self.dlg.use_layer.stateChanged.connect(self.enableLayer)
66
+        self.dlg.api_key.textChanged.connect(self.keyWriter)
67
+        
68
+        # Populate field ID dynamically when combobox selection changes
69
+        self.dlg.layer.currentIndexChanged.connect(self.popBox)
70
+        self.dlg.check_dissolve.stateChanged.connect(self.popBox)
71
+        self.dlg.access_map.clicked.connect(self.initMapTool)
72
+    
73
+    
74
+    def enableLayer(self):
75
+        if self.dlg.use_layer.isChecked() == True:
76
+            self.dlg.frame_2.setEnabled(True)
77
+        else:
78
+            self.dlg.frame_2.setEnabled(False)
79
+            
80
+    
81
+    def valueChanged(self):
82
+        self.iso_max = self.dlg.iso_max.value()
83
+        self.iso_int = self.dlg.iso_int.value() * 60
84
+        self.iso_mode = self.dlg.mode.currentText()
85
+        self.iso_method = self.dlg.method.currentText()
86
+        
87
+    
88
+    def keyWriter(self):
89
+        with open(os.path.join(self.script_dir, "apikey.txt"), 'w') as key:
90
+            self.api_key = self.dlg.api_key.text()
91
+            return key.write(self.dlg.api_key.text())
92
+        
93
+        
94
+    # Populate field ID
95
+    def popBox(self):              
96
+        if self.dlg.check_dissolve.isChecked() == True:
97
+            self.dlg.id_field.setEnabled(True)
98
+            self.dlg.id_field.clear()
99
+            layer_list = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == self.dlg.layer.currentText()]
100
+            if layer_list:
101
+                layer_selected = layer_list[0]
102
+                fields_selected = layer_selected.fields()
103
+                for field in fields_selected:
104
+                    self.dlg.id_field.addItem(field.name())
105
+        else:
106
+            self.dlg.id_field.setEnabled(False)
107
+        return
108
+    
109
+        
110
+    # Connect to PointTool and set as mapTool
111
+    def initMapTool(self):
112
+        sending_button = self.dlg.sender().objectName()
113
+        self.mapTool = osm_tools_pointtool.PointTool(qgis.utils.iface.mapCanvas(), sending_button)        
114
+        self.iface.mapCanvas().setMapTool(self.mapTool)     
115
+        self.mapTool.canvasClicked.connect(self.pointAnalysis)
116
+        
117
+        
118
+    def accRequest(self, point_in):
119
+        QApplication.setOverrideCursor(Qt.WaitCursor)
120
+        #geometry_in = QgsGeometry.fromPoint(point_in)
121
+        x, y = point_in.asPoint()
122
+        
123
+        #TODO: 'method' does not seem to be available over get()?! Ask GIScience team
124
+        req = "{}api_key={}&position={},{}&minutes={}&interval={}&routePreference={}".format(self.url, 
125
+                                                                self.api_key, 
126
+                                                                x, 
127
+                                                                y,
128
+                                                                self.iso_max,
129
+                                                                self.iso_int,
130
+                                                                self.iso_mode)
131
+        response = requests.get(req)
132
+        root = ET.fromstring(response.content)
133
+        access_path = root.find("aas:Response/"
134
+                                "aas:AccessibilityResponse/"
135
+                                "aas:AccessibilityGeometry",
136
+                                self.ns)
137
+        
138
+        QApplication.restoreOverrideCursor()
139
+        
140
+        isochrone_list = []
141
+        feat_list = []
142
+
143
+        try:
144
+            for isochrone in access_path.findall("aas:Isochrone", self.ns):
145
+                feat_out = QgsFeature()
146
+                
147
+                coord_ext_list = []
148
+                coord_int_list = []
149
+                
150
+                # First find the exterior ring
151
+                for coords_ext in isochrone.findall("aas:IsochroneGeometry/"
152
+                                                "gml:Polygon/"
153
+                                                "gml:exterior/"
154
+                                                "gml:LinearRing/"
155
+                                                "gml:pos",
156
+                                                self.ns):
157
+                    coords_ext_tuple = tuple([float(coord) for coord in coords_ext.text.split(" ")])
158
+                    qgis_coords_ext = QgsPoint(coords_ext_tuple[0], coords_ext_tuple[1])
159
+                    coord_ext_list.append(qgis_coords_ext)
160
+                
161
+                # Then find all interior rings
162
+                for ring_int in isochrone.findall("aas:IsochroneGeometry/"
163
+                                            "gml:Polygon/"
164
+                                            "gml:interior",
165
+                                            self.ns):
166
+                    int_poly = []
167
+                    for coords_int in ring_int.findall("gml:LinearRing/"
168
+                                            "gml:pos",
169
+                                            self.ns):
170
+                        coords_int_tuple = tuple([float(coord) for coord in coords_int.text.split(" ")])
171
+                        qgis_coords_int = QgsPoint(coords_int_tuple[0], coords_int_tuple[1])
172
+                        int_poly.append(qgis_coords_int)
173
+                    coord_int_list.append(int_poly)
174
+                        
175
+                feat_out.setGeometry(QgsGeometry.fromPolygon([coord_ext_list] + coord_int_list))
176
+                feat_list.append(feat_out)
177
+                isochrone_list.append(float(isochrone.get("time"))/60)
178
+        except (AttributeError, TypeError):
179
+            msg = "Request is not valid! Check parameters. TIP: Coordinates must plot within 1 km of a road."
180
+            qgis.utils.iface.messageBar().pushMessage(msg, level = qgis.gui.QgsMessageBar.CRITICAL)
181
+            return
182
+        
183
+        return feat_list, isochrone_list
184
+
185
+        
186
+    def pointAnalysis(self, point):
187
+        try:
188
+            point_geometry = QgsGeometry.fromPoint(point)
189
+            feat_list, isochrone_list = self.accRequest(point_geometry)
190
+            
191
+            _point_geocode = osm_tools_geocode.Geocode(self.dlg, self.api_key)
192
+            loc_dict = _point_geocode.reverseGeocode(point_geometry)
193
+        except (AttributeError, TypeError):
194
+            msg = "Request is not valid! Check parameters. TIP: Coordinates must plot within 1 km of a road."
195
+            qgis.utils.iface.messageBar().pushMessage(msg, level = qgis.gui.QgsMessageBar.CRITICAL)
196
+            return
197
+        
198
+        out_str = u"Long: {0:.3f}, Lat:{1:.3f}\n{2}\n{3}\n{4}".format(loc_dict.get('Lon', ""),
199
+                                                        loc_dict.get('Lat', ""),
200
+                                                        loc_dict.get('MUNICIPALI', "NA"),
201
+                                                        loc_dict.get('COUNTRYSUB', "NA"),
202
+                                                        loc_dict.get('COUNTRY', "NA")
203
+                                                        )
204
+        self.dlg.access_text.setText(out_str)
205
+        
206
+        layer_out = QgsVectorLayer("Polygon?crs=EPSG:4326", "AA_{0:.3f},{1:.3f}".format(loc_dict['Lon'], loc_dict['Lat']), "memory")
207
+        layer_out_point = QgsVectorLayer("Point?crs=EPSG:4326", "Point_{0:.3f},{1:.3f}".format(loc_dict['Lon'], loc_dict['Lat']), "memory")
208
+        
209
+        layer_out_prov = layer_out.dataProvider()
210
+        layer_out_prov.addAttributes([QgsField("AA_MINS", QVariant.Int)])
211
+        layer_out_prov.addAttributes([QgsField("AA_MODE", QVariant.String)])
212
+        layer_out.updateFields()
213
+        
214
+        layer_out_point_prov = layer_out_point.dataProvider()
215
+        layer_out_point_prov.addAttributes([QgsField("LAT", QVariant.String)])
216
+        layer_out_point_prov.addAttributes([QgsField("LONG", QVariant.String)])
217
+        layer_out_point_prov.addAttributes([QgsField("DIST_INPUT", QVariant.String)])
218
+        layer_out_point_prov.addAttributes([QgsField("BULIDINGNA", QVariant.String)])
219
+        layer_out_point_prov.addAttributes([QgsField("OFFICIALNA", QVariant.String)])
220
+        layer_out_point_prov.addAttributes([QgsField("NUMBER", QVariant.String)])
221
+        layer_out_point_prov.addAttributes([QgsField("POSTALCODE", QVariant.String)])
222
+        layer_out_point_prov.addAttributes([QgsField("MUNICIPALI", QVariant.String)])
223
+        layer_out_point_prov.addAttributes([QgsField("COUNTRYSUB", QVariant.String)])
224
+        layer_out_point_prov.addAttributes([QgsField("COUNTRY", QVariant.String)])
225
+        layer_out_point.updateFields()
226
+        
227
+        # Add clicked point feature to point feature class
228
+        point_out = QgsFeature()
229
+        point_out.setGeometry(point_geometry)
230
+        point_out.setAttributes([loc_dict.get("Lat", None),
231
+                                loc_dict.get("Lon", None),
232
+                                loc_dict.get("DIST_INPUT", None),
233
+                                loc_dict.get("BULIDINGNA", None),
234
+                                loc_dict.get("OFFICIALNA", None),
235
+                                loc_dict.get("NUMBER", None),
236
+                                loc_dict.get("POSTALCODE", None),
237
+                                loc_dict.get("MUNICIPALI", None),
238
+                                loc_dict.get("COUNTRYSUB", None),
239
+                                loc_dict.get('COUNTRY', None)
240
+                                ])
241
+        layer_out_point_prov.addFeatures([point_out])
242
+        
243
+        for ind, feat in enumerate(feat_list):
244
+            feat.setAttributes([isochrone_list[ind], self.iso_mode])
245
+            layer_out_prov.addFeatures([feat])
246
+
247
+        layer_out.updateExtents()
248
+        layer_out_point.updateExtents()
249
+        
250
+        QgsMapLayerRegistry.instance().addMapLayer(layer_out_point)
251
+        QgsMapLayerRegistry.instance().addMapLayer(layer_out)
252
+
253
+        fields_diss = ["AA_MINS"]
254
+        self.dissolveFields(layer_out, fields_diss)
255
+        
256
+        # Unset Map Tool
257
+        self.iface.mapCanvas().unsetMapTool(self.mapTool)
258
+        self.mapTool = None
259
+
260
+    def iterAnalysis(self):
261
+        allLayers = self.iface.legendInterface().layers()
262
+                
263
+        # Determine selected layer
264
+        for layer in allLayers:
265
+            if layer.name() == self.dlg.layer.currentText():
266
+                acc_input_lyr = layer
267
+                break
268
+        #TODO: Maybe reproject when other than WGS84?! Now it`s just closing the window
269
+        if osm_tools.CheckCRS(self, acc_input_lyr.crs().authid()) == False:
270
+            return
271
+        
272
+        # Define polygon .shp
273
+        layer_out = QgsVectorLayer("Polygon?crs=EPSG:4326", "{}_AA_{}".format(acc_input_lyr.name(),self.iso_mode), "memory")
274
+        layer_out_prov = layer_out.dataProvider()
275
+        for field in acc_input_lyr.fields():
276
+            layer_out_prov.addAttributes([field])
277
+        layer_out_prov.addAttributes([QgsField("AA_MINS", QVariant.Int)])
278
+        layer_out_prov.addAttributes([QgsField("AA_MODE", QVariant.String)])
279
+        layer_out.updateFields()
280
+        
281
+        features = acc_input_lyr.getFeatures()
282
+        
283
+        # Progress Bar
284
+        feature_count = acc_input_lyr.featureCount()
285
+        progressMessageBar = self.iface.messageBar().createMessage("Requesting analysis from ORS...")
286
+        progress = QProgressBar()
287
+        progress.setMaximum(feature_count)
288
+        progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
289
+        progressMessageBar.layout().addWidget(progress)
290
+        self.iface.messageBar().pushWidget(progressMessageBar, self.iface.messageBar().INFO)
291
+                
292
+        for i, feat_in in enumerate(features):
293
+            progress.setValue(i)
294
+            
295
+            feat_list, isochrone_list = self.accRequest(feat_in.geometry())
296
+            
297
+            for ind, feat in enumerate(feat_list):                
298
+                feat.setAttributes(feat_in.attributes() + [isochrone_list[ind], self.iso_mode])
299
+                layer_out_prov.addFeatures([feat])
300
+
301
+            layer_out.updateExtents()
302
+        
303
+        id_field = self.dlg.id_field.currentText()
304
+        fields_diss = ["AA_MINS", id_field]
305
+        
306
+        QgsMapLayerRegistry.instance().addMapLayer(layer_out)
307
+        self.dissolveFields(layer_out, fields_diss)
308
+    
309
+        
310
+    def dissolveFields(self, layer_out, fields_diss):        
311
+        # Dissolve output for interval 'AA_MINS' and id_layer, remove non-dissolved layer
312
+        
313
+        processing.runandload("qgis:dissolve", layer_out , False,
314
+                          fields_diss, "memory:dissolved")
315
+        layer_dissolved = QgsMapLayerRegistry.instance().mapLayersByName("Dissolved")[-1]
316
+        layer_dissolved.setLayerName(layer_out.name())
317
+        QgsMapLayerRegistry.instance().removeMapLayers([layer_out.id()])

+ 41
- 0
osm_tools_dialog.py View File

@@ -0,0 +1,41 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+/***************************************************************************
4
+ ORStoolsDialog
5
+                                 A QGIS plugin
6
+ falk
7
+                             -------------------
8
+        begin                : 2017-02-01
9
+        git sha              : $Format:%H$
10
+        copyright            : (C) 2017 by nils
11
+        email                : nils
12
+ ***************************************************************************/
13
+
14
+/***************************************************************************
15
+ *                                                                         *
16
+ *   This program is free software; you can redistribute it and/or modify  *
17
+ *   it under the terms of the GNU General Public License as published by  *
18
+ *   the Free Software Foundation; either version 2 of the License, or     *
19
+ *   (at your option) any later version.                                   *
20
+ *                                                                         *
21
+ ***************************************************************************/
22
+"""
23
+
24
+import os
25
+
26
+from PyQt4 import QtGui, uic
27
+
28
+FORM_CLASS, _ = uic.loadUiType(os.path.join(
29
+    os.path.dirname(__file__), 'osm_tools_dialog_base.ui'))
30
+
31
+
32
+class OSMtoolsDialog(QtGui.QDialog, FORM_CLASS):
33
+    def __init__(self, parent=None):
34
+        """Constructor."""
35
+        super(OSMtoolsDialog, self).__init__(parent)
36
+        # Set up the user interface from Designer.
37
+        # After setupUI you can access any designer object by doing
38
+        # self.<objectname>, and you can use autoconnect slots - see
39
+        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
40
+        # #widgets-and-dialogs-with-auto-connect
41
+        self.setupUi(self)

+ 1319
- 0
osm_tools_dialog_base.ui
File diff suppressed because it is too large
View File


+ 64
- 0
osm_tools_geocode.py View File

@@ -0,0 +1,64 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Created on Tue Feb 07 00:34:21 2017
4
+
5
+@author: nnolde
6
+"""
7
+
8
+from PyQt4.QtCore import *
9
+from PyQt4.QtGui import *
10
+
11
+from qgis.core import *
12
+from qgis.gui import * 
13
+import qgis.utils
14
+
15
+import requests
16
+import xml.etree.ElementTree as ET
17
+
18
+class Geocode:
19
+    def __init__(self, dlg, api_key):
20
+        self.dlg = dlg
21
+        self.url = r"http://openls.geog.uni-heidelberg.de/geocode?"
22
+        self.ns = {'gml': 'http://www.opengis.net/gml',
23
+                  'xls': "http://www.opengis.net/xls",
24
+                  'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
25
+                  
26
+        # API parameters
27
+        self.api_key = api_key
28
+        
29
+        self.iface = qgis.utils.iface    
30
+        
31
+    def reverseGeocode(self, point_in):
32
+        x, y = point_in.asPoint()
33
+        req = "{}api_key={}&pos={} {}".format(self.url, 
34
+                                            self.api_key, 
35
+                                            x, 
36
+                                            y)
37
+        response = requests.get(req)
38
+        root = ET.fromstring(response.content)
39
+        access_path = root.find("xls:Response/"
40
+                                "xls:ReverseGeocodeResponse/"
41
+                                "xls:ReverseGeocodedLocation",
42
+                                self.ns)
43
+        
44
+        loc_place_dict = dict()
45
+        
46
+        pos = access_path.find("gml:Point/gml:pos", self.ns).text
47
+        x, y  = pos.split(" ")
48
+        loc_place_dict['Lon'] = float(x)
49
+        loc_place_dict['Lat'] = float(y)
50
+        loc_place_dict['DIST_INPUT'] = access_path.find("xls:SearchCentreDistance", self.ns).get('value')
51
+        loc_list = access_path.findall("xls:StreetAddress/xls:Building", self.ns)
52
+        for element in loc_list:
53
+            loc_place_dict[element.keys()[0][:10].upper()] = element.get(element.keys()[0], "")
54
+        loc_list = access_path.findall("xls:StreetAddress/xls:PostalCode", self.ns)
55
+        for element in loc_list:
56
+            loc_place_dict['POSTALCODE'] = element.text
57
+        loc_list = access_path.findall("xls:Address/xls:StreetAddress/xls:Street", self.ns)
58
+        for element in loc_list:
59
+            loc_place_dict[element.keys()[0][:10].upper()] = element.get(element.keys()[0], "")
60
+        loc_list = access_path.findall("xls:Address/xls:Place", self.ns)
61
+        for element in loc_list:
62
+            loc_place_dict[element.get('type')[:10].upper()] = unicode(element.text)
63
+                           
64
+        return loc_place_dict

+ 70
- 0
osm_tools_pointtool.py View File

@@ -0,0 +1,70 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Created on Mon Feb 06 23:35:16 2017
4
+
5
+@author: nnolde
6
+"""
7
+from PyQt4.QtCore import *
8
+from PyQt4.QtGui import *
9
+
10
+from qgis.core import *
11
+from qgis.gui import *
12
+
13
+import os
14
+
15
+# Find cursor icon in plugin tree
16
+def resolve(name, basepath=None):
17
+    if not basepath:
18
+      basepath = os.path.dirname(os.path.realpath(__file__))
19
+    return os.path.join(basepath, name)
20
+    
21
+
22
+class PointTool(QgsMapTool):   
23
+    def __init__(self, canvas, button):
24
+        QgsMapTool.__init__(self, canvas)
25
+        self.canvas = canvas    
26
+        self.button = button
27
+        self.imgdir = resolve('icon_locate.png')
28
+        self.cursor = QCursor(QPixmap(self.imgdir).scaledToWidth(24), 12, 12)
29
+        
30
+        #QApplication.setOverrideCursor(QCursor(QPixmap('/icon_locate.png')))
31
+
32
+    def canvasPressEvent(self, event):
33
+        pass
34
+
35
+    def canvasMoveEvent(self, event):
36
+        pass
37
+    
38
+    canvasClicked = pyqtSignal(['QgsPoint', 'QString', 'Qt::MouseButton'])
39
+    def canvasReleaseEvent(self, event):
40
+        #Get the click and emit a transformed point
41
+        
42
+        crsSrc = self.canvas.mapRenderer().destinationCrs()
43
+        crsWGS = QgsCoordinateReferenceSystem(4326)
44
+    
45
+        point_oldcrs = self.toMapCoordinates(event.pos())
46
+        
47
+        xform = QgsCoordinateTransform(crsSrc, crsWGS)
48
+        point_newcrs = xform.transform(point_oldcrs)
49
+        
50
+        QApplication.restoreOverrideCursor()
51
+        
52
+        self.canvasClicked.emit(point_newcrs, self.button, event.button())
53
+        
54
+    def activate(self):
55
+        QApplication.setOverrideCursor(self.cursor)
56
+        #self.canvas.setCursor(self.cursor)
57
+        
58
+        print "WTFe"
59
+
60
+    def deactivate(self):
61
+        pass
62
+
63
+    def isZoomTool(self):
64
+        return False
65
+
66
+    def isTransient(self):
67
+        return False
68
+
69
+    def isEditTool(self):
70
+        return True

+ 355
- 0
osm_tools_routing.py View File

@@ -0,0 +1,355 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Created on Wed Feb 08 21:14:48 2017
4
+
5
+@author: nnolde
6
+"""
7
+
8
+from PyQt4.QtCore import *
9
+from PyQt4.QtGui import *
10
+
11
+from qgis.core import *
12
+from qgis.gui import * 
13
+import qgis.utils
14
+
15
+import requests
16
+import xml.etree.ElementTree as ET
17
+import os.path
18
+import re
19
+import itertools
20
+
21
+import osm_tools_pointtool
22
+
23
+class routing:
24
+    def __init__(self, dlg):
25
+        self.dlg = dlg
26
+        self.url = r"http://openls.geog.uni-heidelberg.de/route?"
27
+        self.ns = {'gml': 'http://www.opengis.net/gml',
28
+                  'xls': "http://www.opengis.net/xls",
29
+                  'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
30
+        
31
+        # GUI init
32
+        self.dlg.start_layer.clear()
33
+        self.dlg.end_layer.clear()
34
+        self.dlg.mode_travel.clear()
35
+        self.dlg.mode_routing.clear()
36
+        self.dlg.mode_travel.addItem('Car')
37
+        self.dlg.mode_travel.addItem('Bicycle')
38
+        self.dlg.mode_travel.addItem('Pedestrian')
39
+        self.dlg.mode_travel.addItem('HeavyVehicle')
40
+        self.dlg.mode_routing.addItem('Fastest')
41
+        self.dlg.mode_routing.addItem('Shortest')
42
+        
43
+        for layer in qgis.utils.iface.legendInterface().layers():
44
+            layerType = layer.type()
45
+            if layerType == QgsMapLayer.VectorLayer and layer.wkbType() == QGis.WKBPoint:
46
+                self.dlg.start_layer.addItem(layer.name())
47
+                self.dlg.end_layer.addItem(layer.name())
48
+                
49
+        self.layer_start = None
50
+        self.layer_end = None
51
+        
52
+        self.startPopBox()
53
+        self.endPopBox()
54
+                  
55
+        # API parameters
56
+        self.api_key = self.dlg.api_key.text()
57
+        self.mode_travel = self.dlg.mode_travel.currentText()
58
+        self.mode_routing = self.dlg.mode_routing.currentText()
59
+        self.speed_max = self.dlg.speed_max.value()
60
+        
61
+        self.iface = qgis.utils.iface
62
+        
63
+        self.script_dir = os.path.dirname(os.path.abspath(__file__))
64
+        
65
+        # Connect events to signals
66
+        self.dlg.start_radio_map.toggled.connect(self.startPopBox)
67
+        self.dlg.end_radio_map.toggled.connect(self.endPopBox)
68
+        self.dlg.start_layer.currentIndexChanged.connect(self.startPopBox)
69
+        self.dlg.end_layer.currentIndexChanged.connect(self.endPopBox)
70
+        self.dlg.mode_travel.currentIndexChanged.connect(self.valueChanged)
71
+        self.dlg.mode_routing.currentIndexChanged.connect(self.valueChanged)
72
+        self.dlg.speed_max.valueChanged.connect(self.valueChanged)
73
+        self.dlg.add_start_button.clicked.connect(self.initMapTool)
74
+        self.dlg.add_end_button.clicked.connect(self.initMapTool)
75
+        self.dlg.add_via_button.clicked.connect(self.initMapTool)
76
+        self.dlg.add_via_button_clear.clicked.connect(self.clearVia)
77
+        self.dlg.api_key.textChanged.connect(self.keyWriter)
78
+    
79
+    def clearVia(self):
80
+        self.dlg.add_via.setText("Long,Lat")
81
+    
82
+    def startPopBox(self):
83
+        if self.dlg.start_radio_layer.isChecked():
84
+            self.dlg.add_start_button.setEnabled(False)
85
+            self.dlg.start_layer.setEnabled(True)
86
+            self.dlg.start_layer_id.setEnabled(True)
87
+            self.dlg.start_layer_id.clear()
88
+            layer_list = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == self.dlg.start_layer.currentText()]
89
+            if layer_list:
90
+                layer_selected = layer_list[0]
91
+                fields_selected = layer_selected.fields()
92
+                for field in fields_selected:
93
+                    self.dlg.start_layer_id.addItem(field.name())
94
+                
95
+            # Determine selected layer
96
+            for layer in qgis.utils.iface.legendInterface().layers():
97
+                if layer.name() == self.dlg.start_layer.currentText():
98
+                    self.layer_start = layer
99
+                    break
100
+        else:
101
+            self.dlg.start_layer_id.setEnabled(False)
102
+            self.dlg.start_layer.setEnabled(False)
103
+            self.dlg.add_start_button.setEnabled(True)
104
+            
105
+        if self.dlg.end_radio_layer.isChecked() and self.dlg.start_radio_layer.isChecked():
106
+            self.dlg.radio_one.setEnabled(True)
107
+            self.dlg.radio_many.setEnabled(True)
108
+        else:
109
+            self.dlg.radio_one.setEnabled(False)
110
+            self.dlg.radio_many.setEnabled(False)
111
+            
112
+        return
113
+    
114
+        
115
+    def endPopBox(self):
116
+        if self.dlg.end_radio_layer.isChecked():
117
+            self.dlg.add_end_button.setEnabled(False)
118
+            self.dlg.end_layer.setEnabled(True)
119
+            self.dlg.end_layer_id.setEnabled(True)
120
+            self.dlg.end_layer_id.clear()
121
+            layer_list = [lyr for lyr in QgsMapLayerRegistry.instance().mapLayers().values() if lyr.name() == self.dlg.end_layer.currentText()]
122
+            if layer_list:
123
+                layer_selected = layer_list[0]
124
+                fields_selected = layer_selected.fields()
125
+                for field in fields_selected:
126
+                    self.dlg.end_layer_id.addItem(field.name())
127
+                    
128
+            # Determine selected layer
129
+            for layer in qgis.utils.iface.legendInterface().layers():
130
+                if layer.name() == self.dlg.end_layer.currentText():
131
+                    self.layer_end = layer
132
+                    break            
133
+        else:
134
+            self.dlg.end_layer_id.setEnabled(False)
135
+            self.dlg.end_layer.setEnabled(False)
136
+            self.dlg.add_end_button.setEnabled(True)
137
+        
138
+        if self.dlg.end_radio_layer.isChecked() and self.dlg.start_radio_layer.isChecked():
139
+            self.dlg.radio_one.setEnabled(True)
140
+            self.dlg.radio_many.setEnabled(True)
141
+        else:
142
+            self.dlg.radio_one.setEnabled(False)
143
+            self.dlg.radio_many.setEnabled(False)
144
+            
145
+        return
146
+        
147
+    
148
+    # Event for GUI Signals
149
+    def valueChanged(self):
150
+        self.mode_travel = self.dlg.mode_travel.currentText()
151
+        self.mode_routing = self.dlg.mode_routing.currentText()
152
+        self.speed_max = self.dlg.speed_max.value()
153
+        
154
+    
155
+    # Event for API key change
156
+    def keyWriter(self):
157
+        with open(os.path.join(self.script_dir, "apikey.txt"), 'w') as key:
158
+            self.api_key = self.dlg.api_key.text()
159
+            return key.write(self.dlg.api_key.text())
160
+            
161
+    
162
+    # Connect to PointTool and set as mapTool
163
+    def initMapTool(self):
164
+        sending_button = self.dlg.sender().objectName() 
165
+        self.mapTool = osm_tools_pointtool.PointTool(qgis.utils.iface.mapCanvas(), sending_button)        
166
+        self.iface.mapCanvas().setMapTool(self.mapTool)     
167
+        self.mapTool.canvasClicked.connect(self.writeText)
168
+
169
+        
170
+    # Write map coordinates to text fields
171
+    def writeText(self, point, button):
172
+        x, y = point
173
+        
174
+        if button == self.dlg.add_start_button.objectName():
175
+            self.dlg.add_start.setText("{0:.5f},{1:.5f}".format(x, y))
176
+            
177
+        if button == self.dlg.add_end_button.objectName():
178
+            self.dlg.add_end.setText("{0:.5f},{1:.5f}".format(x, y))
179
+            
180
+        if button == self.dlg.add_via_button.objectName():
181
+            self.dlg.add_via.setText("{0:.5f},{1:.5f}\n".format(x, y))
182
+            
183
+        
184
+    def route(self):
185
+        
186
+#        if osm_tools.CheckCRS(self, self.layer_start.crs().authid()) == False:
187
+#            return
188
+#        if osm_tools.CheckCRS(self, self.layer_end.crs().authid()) == False:
189
+#            return
190
+        
191
+        # Create memory routing layer with fields
192
+        layer_out = QgsVectorLayer("LineString?crs=EPSG:4326", "Route", "memory")
193
+        layer_out_prov = layer_out.dataProvider()
194
+        layer_out_prov.addAttributes([QgsField("DISTANCE", QVariant.Double)])
195
+        layer_out_prov.addAttributes([QgsField("TIME_H", QVariant.Int)])
196
+        layer_out_prov.addAttributes([QgsField("TIME_MIN", QVariant.Int)])
197
+        layer_out_prov.addAttributes([QgsField("TIME_SEC", QVariant.Int)])
198
+        layer_out_prov.addAttributes([QgsField("MODE", QVariant.String)])
199
+        layer_out_prov.addAttributes([QgsField("PREF", QVariant.String)])
200
+        #layer_out_prov.addAttributes([QgsField("SPEED_MAX", QVariant.String)])
201
+        layer_out_prov.addAttributes([QgsField("FROM_LAT", QVariant.Double)])
202
+        layer_out_prov.addAttributes([QgsField("FROM_LONG", QVariant.Double)])
203
+        layer_out_prov.addAttributes([QgsField("TO_LAT", QVariant.Double)])
204
+        layer_out_prov.addAttributes([QgsField("TO_LONG", QVariant.Double)])
205
+        layer_out_prov.addAttributes([QgsField("FROM_ID", QVariant.String)])
206
+        layer_out_prov.addAttributes([QgsField("TO_ID", QVariant.String)])
207
+        
208
+        layer_out.updateFields()
209
+        
210
+        start_features = []
211
+        end_features = []
212
+        start_ids = []
213
+        end_ids = []
214
+
215
+        # Create start features
216
+        if self.dlg.start_radio_layer.isChecked():
217
+            start_feat = self.layer_start.getFeatures()
218
+            field_id = self.layer_start.fieldNameIndex(self.dlg.start_layer_id.currentText())
219
+            for feat in start_feat:
220
+                x, y = feat.geometry().asPoint()
221
+                start_features.append(",".join([str(x), str(y)]))
222
+                start_ids.append(feat.attributes()[field_id])
223
+        else:
224
+            start_features.append(self.dlg.add_start.text())
225
+            start_ids.append(self.dlg.add_start.text())
226
+            
227
+        # Create end features
228
+        if self.dlg.end_radio_layer.isChecked():
229
+            end_feat = self.layer_end.getFeatures()
230
+            field_id = self.layer_end.fieldNameIndex(self.dlg.end_layer_id.currentText())
231
+            for feat in end_feat:
232
+                x, y = feat.geometry().asPoint()
233
+                end_features.append(",".join([str(x), str(y)]))
234
+                end_ids.append(feat.attributes()[field_id])
235
+        else:
236
+            end_features.append(self.dlg.add_end.text())
237
+            end_ids.append(self.dlg.add_end.text())
238
+            
239
+        # Rules for creating routing features
240
+        if len(start_features) == 1:
241
+            if len(end_features) == 1:
242
+                route_features = zip(start_features, end_features)
243
+                route_ids = zip(start_ids, end_ids)
244
+            else:
245
+                route_features = zip(itertools.cycle(start_features), end_features)
246
+                route_ids = zip(itertools.cycle(start_ids), end_ids)
247
+        else:
248
+            if len(end_features) == 1:
249
+                route_features = zip(start_features, itertools.cycle(end_features))
250
+                route_ids = zip(start_ids, itertools.cycle(end_ids))
251
+            else:
252
+                if self.dlg.radio_one.isChecked():
253
+                    route_features = zip(start_features, end_features)
254
+                    route_ids = zip(start_ids, end_ids)
255
+                else:
256
+                    route_features = list(itertools.product(start_features, end_features))
257
+                    route_ids = list(itertools.product(start_ids, end_ids))
258
+
259
+        # Read route details from GUI
260
+        route_via = " ".join(self.dlg.add_via.text().split("\n")[:-1])
261
+        
262
+        # Set up progress bar
263
+        route_count = len(route_features)
264
+        progressMessageBar = self.iface.messageBar().createMessage("Requesting routes from ORS...")
265
+        progress = QProgressBar()
266
+        progress.setMaximum(route_count)
267
+        progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
268
+        progressMessageBar.layout().addWidget(progress)
269
+        self.iface.messageBar().pushWidget(progressMessageBar, self.iface.messageBar().INFO)
270
+        
271
+        for i, route in enumerate(route_features):
272
+            # Skip route if start and end are identical
273
+            if route[0] == route[1]:
274
+                continue
275
+            else:
276
+                try:
277
+                    # Create URL
278
+                    req = "{}api_key={}&start={}&end={}&routepref={}&weighting={}&maxspeed={}&instructions=False".format(self.url, 
279
+                                                        self.api_key, 
280
+                                                        route[0],
281
+                                                        route[1],
282
+                                                        self.mode_travel,
283
+                                                        self.mode_routing,
284
+                                                        self.speed_max
285
+                                                        )
286
+                    print req
287
+                    if route_via != "":
288
+                        req += "&via={}".format(route_via)
289
+                        
290
+                    # Get response from API and read into element tree
291
+                    response = requests.get(req)
292
+                    root = ET.fromstring(response.content)
293
+                    access_path = root.find("xls:Response/"
294
+                                            "xls:DetermineRouteResponse",
295
+                                            self.ns)
296
+                    
297
+                    feat_out = QgsFeature()
298
+                    
299
+                    # Read all coordinates
300
+                    coords_list = []
301
+                    for coords in access_path.findall("xls:RouteGeometry/gml:LineString/gml:pos", self.ns):
302
+                        coords_tuple = tuple([float(coord) for coord in coords.text.split(" ")])
303
+                        qgis_coords = QgsPoint(coords_tuple[0], coords_tuple[1])
304
+                        coords_list.append(qgis_coords)
305
+                    
306
+                    # Read total time
307
+                    time_path = access_path.find("xls:RouteSummary/xls:TotalTime", self.ns)
308
+                    time_text = time_path.text
309
+                    if 'D' not in time_text:
310
+                        time_text = re.sub(r'(P)', r'P0D', time_text)
311
+                    if 'H' not in time_text:
312
+                        time_text = re.sub(r'(T)', r'T0H', time_text)
313
+                    if 'M' not in time_text:
314
+                        time_text = re.sub(r'(H)', r'H0M', time_text)
315
+                    
316
+                    time_list = list(reversed(re.split('DT|H|M', time_text[1:-1])))
317
+                    while len(time_list) < 4:
318
+                        time_list.append('0')
319
+                    secs, mins, hours, days = [int(x) for x in time_list]
320
+                    hours += (days*24)
321
+                    #hours = "{0:.3f}".format(hours)
322
+                                         
323
+                    # Read total distance
324
+                    distance = float(access_path.find("xls:RouteSummary/xls:TotalDistance", self.ns).get("value"))
325
+                    
326
+                    # Read X and Y
327
+                    route_start_x, route_start_y = [float(coord) for coord in route[0].split(",")]
328
+                    route_end_x, route_end_y = [float(coord) for coord in route[1].split(",")]
329
+                        
330
+                    # Set feature geometry and attributes
331
+                    feat_out.setGeometry(QgsGeometry.fromPolyline(coords_list))
332
+                    feat_out.setAttributes([distance,
333
+                                            hours,
334
+                                            mins,
335
+                                            secs,
336
+                                            self.mode_travel,
337
+                                            self.mode_routing,
338
+                                            route_start_y,
339
+                                            route_start_x,
340
+                                            route_end_y,
341
+                                            route_end_x,
342
+                                            route_ids[i][0],
343
+                                            route_ids[i][1]]) 
344
+                    
345
+                    layer_out_prov.addFeatures([feat_out])
346
+                    
347
+                    progress.setValue(i)    
348
+                except (AttributeError, TypeError):
349
+                    msg = "Request is not valid! Check parameters. TIP: Coordinates must plot within 1 km of a road."
350
+                    qgis.utils.iface.messageBar().pushMessage(msg, level = qgis.gui.QgsMessageBar.CRITICAL)
351
+                    return
352
+                
353
+        layer_out.updateExtents()
354
+
355
+        QgsMapLayerRegistry.instance().addMapLayer(layer_out)

+ 3
- 3
pb_tool.cfg View File

@@ -38,14 +38,14 @@
38 38
 [plugin]
39 39
 # Name of the plugin. This is the name of the directory that will
40 40
 # be created in .qgis2/python/plugins
41
-name: ORStools
41
+name: OSMtools
42 42
 
43 43
 [files]
44 44
 # Python  files that should be deployed with the plugin
45
-python_files: __init__.py ors_tools.py ors_tools_dialog.py
45
+python_files: __init__.py osm_tools.py osm_tools_dialog.py
46 46
 
47 47
 # The main dialog file that is loaded (not compiled)
48
-main_dialog: ors_tools_dialog_base.ui
48
+main_dialog: osm_tools_dialog_base.ui
49 49
 
50 50
 # Other ui files for dialogs you create (these will be compiled)
51 51
 compiled_ui_files: 

+ 1
- 1
resources_rc.py View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 # Resource object code
4 4
 #
5
-# Created: Sa 4. Mrz 03:20:16 2017
5
+# Created: Thu 27. Apr 23:47:59 2017
6 6
 #      by: The Resource Compiler for PyQt (Qt v4.8.5)
7 7
 #
8 8
 # WARNING! All changes made in this file will be lost!

Loading…
Cancel
Save