-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQgisDwd.py
276 lines (220 loc) · 11.3 KB
/
QgisDwd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# before importing this module, make sure the PyQgis environment is correctly set up.
# for example:
# Append QGIS Python library to python search path
#sys.path.append(QGIS_PREFIX_PATH + '/python')
#sys.path.append(QGIS_PREFIX_PATH + '/python/plugins')
# Append location of DLLs to current system PATH envrionment variable
#os.environ['PATH'] += ";" + QGIS_PREFIX_PATH + "/bin"
# THIS SEEMS TO WORK!!!
#os.environ['QGIS_PREFIX_PATH'] = QGIS_PREFIX_PATH
# the following is suggested to do, but DOES NOT WORK!!! Setting the prefix path via
# os.environ DOES WORK!
#QgsApplication.setPrefixPath("c:/Program Files/QGIS 2.18/apps/qgis-ltr", False)
import os, sys
from qgis.core import *
import qgis
import os.path
from datetime import datetime
from datetime import timedelta
import time
from PyQt4.QtXml import QDomDocument
from PyQt4.QtCore import QTimer
import processing
from processing.core.Processing import Processing
class QgisDwdException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
def fetchLayerByName(mapRegistry, name):
layers = mapRegistry.mapLayersByName(name)
if len(layers) < 1:
error = "No layer found with name '%s'" % name
raise QgisDwdException(error)
else:
return layers[0]
def joinLayers(joinLayer, joinFieldName, targetLayer, targetFieldName, fieldSubset):
# Set up join parameters
joinObject = QgsVectorJoinInfo()
joinObject.joinLayerId = joinLayer.id()
joinObject.joinFieldName = joinFieldName
joinObject.targetFieldName = targetFieldName
joinObject.memoryCache = True
joinObject.prefix=""
joinObject.setJoinFieldNamesSubset(fieldSubset)
targetLayer.addJoin(joinObject)
return targetLayer
class QgisDwdRenderer(object):
def __init__(self, dataFilePath, displayLayerStyleFile,
composerTemplateFile, resultFolderPath, zoomToLayerFile, useProcessing):
self.dataFilePath = dataFilePath
self.displayLayerStyleFile = displayLayerStyleFile
self.composerTemplateFile = composerTemplateFile
self.resultFolderPath = resultFolderPath
self.zoomToLayerFile = zoomToLayerFile
if not os.path.isdir(self.resultFolderPath):
raise QgisDwdException("result folder '%s' does not exist" % self.resultFolderPath)
if not os.access(self.resultFolderPath, os.W_OK):
raise QgisDwdException("result folder '%s' exists but is not writable" % self.resultFolderPath)
# load template
if not os.path.isfile(self.composerTemplateFile):
raise QgisDwdException("Composer template file '%s' does not exist" % self.composerTemplateFile)
self.composerTemplate = QDomDocument()
templateFile = file(composerTemplateFile)
templateContent = templateFile.read()
templateFile.close()
self.composerTemplate.setContent(templateContent, False)
useGuiLibs = True
self.QGS = QgsApplication([],useGuiLibs) # 2nd parameter needs to be true in order for processing framework to ... work.
self.QGS.initQgis()
self.PROJECT = QgsProject.instance()
self.MAP_REGISTRY = QgsMapLayerRegistry.instance()
self.COMPOSER_TEMPLATE_DOCUMENT = QDomDocument()
if(useProcessing):
Processing.initialize()
self.DATA_LAYER_NAME_FORMAT = "%s station_data"
def fetchAndFilterDataLayerForDatestring(self, datestring):
dataLayerName = self.DATA_LAYER_NAME_FORMAT % datestring[:6]
dataLayer=fetchLayerByName(self.MAP_REGISTRY, dataLayerName)
# set the actual time and day we want to see
dataLayer.setSubsetString("date="+datestring)
return dataLayer
def renderDisplayLayer(self, displayLayer, dateLabelString, imagepath, imageDpi):
# prepare composition
mapSettings = QgsMapSettings()
composition = QgsComposition(mapSettings)
composition.loadFromTemplate(self.composerTemplate)
# configure map item:
composerMap = composition.getComposerMapById(0)
# zoom to extent if set
if(self.zoomToLayerFile is not None):
zoomToLayer=QgsVectorLayer(self.zoomToLayerFile,"ZoomTo","ogr")
if(zoomToLayer.isValid()):
composerMap.zoomToExtent(zoomToLayer.extent())
else:
raise QgisDwdException("Could not load zoom layer!")
# make sure current data is rendered
composerMap.renderModeUpdateCachedImage()
# configure label with current date
item = composition.getComposerItemById("Datum")
item.setText(dateLabelString)
# set display layer to be displayed:
layerset = [ displayLayer.id() ]
mapSettings.setLayers(layerset)
#export
image = composition.printPageAsRaster(0, dpi = imageDpi)
image.save(imagepath)
def loadDataFiles(self, fromDate, toDate):
# create the list of months to load:
monthList = []
delta = timedelta(days=30) # add a "month"
while(fromDate < toDate):
monthList.append(fromDate.strftime("%Y%m"))
fromDate += delta
for month in monthList:
dataLayerName = self.DATA_LAYER_NAME_FORMAT % month
# check if layer is already loaded:
layers = self.MAP_REGISTRY.mapLayersByName(dataLayerName)
if len(layers) < 1:
dataLayerFile = self.dataFilePath % month
dataLayer = QgsVectorLayer(dataLayerFile, dataLayerName, "ogr")
if not dataLayer.isValid():
raise QgisDwdException("data layer file '%s' not found" % dataLayerFile )
self.MAP_REGISTRY.addMapLayer(dataLayer)
def execute(self, fromDateString, toDateString, renderLimit = 0, useCounterForName = False, imageDpi = 0):
fromDateTime = datetime.strptime(fromDateString+"000000","%Y%m%d%H%M%S")
toDateTime = datetime.strptime(toDateString+"000000","%Y%m%d%H%M%S")
self.loadDataFiles(fromDateTime, toDateTime)
deltaTime = toDateTime - fromDateTime
if(deltaTime.days < 0):
raise QgisDwdException("maxDate earlier than currentDate ?!??")
estimatedFrameCount = ((deltaTime.days * 86400 + deltaTime.seconds ) / 600 ) + 1
currentDate = fromDateTime
counter = 0
try:
while currentDate <= toDateTime:
counter += 1
print "[%06d/%06d] generating map for %s" % (counter, estimatedFrameCount, currentDate.strftime("%Y-%m-%d %H:%M"))
dateLabelString = currentDate.strftime("%d.%m.%Y %H:%M Uhr")
displayLayer = self.prepareDisplayLayer(currentDate)
if useCounterForName:
counterFilename = "%08d" % counter
else:
counterFilename = currentDate.strftime("%Y%m%d%H%M")
imagepath = os.path.join(self.resultFolderPath,"image_composer2_"+counterFilename+".png")
self.renderDisplayLayer(displayLayer, dateLabelString, imagepath, imageDpi)
print " -> '%s'" % imagepath
self.MAP_REGISTRY.removeMapLayer(displayLayer)
if (renderLimit > 0) & (counter >= renderLimit):
break
currentDate = currentDate + timedelta(minutes = 10)
print "script finished writing %d files" % counter
except KeyboardInterrupt:
print "Shutdown requested"
except QgisDwdException as e:
print e
def teardown(self):
self.QGS.exitQgis()
class StaticQgisDwdRenderer(QgisDwdRenderer):
def __init__(self, dataFilePath, displayLayerStyleFile,
composerTemplateFile, resultFolderPath, displayLayerFile, zoomToLayerFile = None ):
super(StaticQgisDwdRenderer,self).__init__(dataFilePath, displayLayerStyleFile,
composerTemplateFile, resultFolderPath, zoomToLayerFile, False)
self.displayLayer = QgsVectorLayer(displayLayerFile, "display layer", "ogr")
self.displayLayer.loadNamedStyle(self.displayLayerStyleFile)
self.MAP_REGISTRY.addMapLayer(self.displayLayer)
def prepareDisplayLayer(self, datetime):
datestring = datetime.strftime("%Y%m%d%H%M")
dataLayer = self.fetchAndFilterDataLayerForDatestring(datestring)
# remove previous joins:
for vectorJoin in self.displayLayer.vectorJoins():
self.displayLayer.removeJoin(vectorJoin.joinLayerId)
# ...and join anew:
self.displayLayer = joinLayers(dataLayer, "station_id", self.displayLayer, "Stations_I", ["TT_10"])
# do some refreshing, not sure if necessary in a standalone script.
#self.displayLayer.reload()
return self.displayLayer
class DynamicQgisDwdRenderer(QgisDwdRenderer):
def __init__(self, dataFilePath, displayLayerStyleFile,
composerTemplateFile, resultFolderPath, stationPointLayerFile, clipLayerFile = None, zoomToLayerFile = None):
super(DynamicQgisDwdRenderer,self).__init__(
dataFilePath, displayLayerStyleFile, composerTemplateFile,
resultFolderPath, zoomToLayerFile, True)
self.stationsLayer=QgsVectorLayer(stationPointLayerFile, "stations_10min_32632", "ogr")
self.MAP_REGISTRY.addMapLayer(self.stationsLayer)
if clipLayerFile is not None:
self.clipLayer=QgsVectorLayer(clipLayerFile, "clip layer", "ogr")
self.MAP_REGISTRY.addMapLayer(self.clipLayer)
else:
self.clipLayer = None
def prepareDisplayLayer(self, datetime):
datestring = datetime.strftime("%Y%m%d%H%M")
dataLayer = self.fetchAndFilterDataLayerForDatestring(datestring)
# create an in-memory copy of the stations
feats = [feat for feat in self.stationsLayer.getFeatures()]
mem_layer = QgsVectorLayer("Point?crs=epsg:32632", "duplicated_layer", "memory")
mem_layer_data = mem_layer.dataProvider()
attr = self.stationsLayer.dataProvider().fields().toList()
mem_layer_data.addAttributes(attr)
mem_layer.updateFields()
mem_layer_data.addFeatures(feats)
# add this to the map registry so we can operate with it
self.MAP_REGISTRY.addMapLayer(mem_layer)
# join the prepared dataLayer to the copy of the stations
mem_layer = joinLayers(dataLayer, "station_id", mem_layer, "Stations_I", ["TT_10"])
# select only stations with valid data points for this date
selection=processing.runalg('qgis:extractbyexpression', mem_layer,'TT_10 IS NOT NULL AND TT_10 > -999',None)
# create a voroinoi map from the selected stations
voronoiResult=processing.runalg('qgis:voronoipolygons', selection["OUTPUT"],20.0,None)
outputLayer = processing.getObject(voronoiResult["OUTPUT"])
self.MAP_REGISTRY.addMapLayer(outputLayer)
if(self.clipLayer is not None):
outputResult=processing.runalg('qgis:clip',outputLayer,self.clipLayer,None)
#print outputLayer.dataProvider().dataSourceUri()
#print CLIP_LAYER.dataProvider().dataSourceUri()
self.MAP_REGISTRY.removeMapLayer(outputLayer)
outputLayer = processing.getObject(outputResult["OUTPUT"])
self.MAP_REGISTRY.addMapLayer(outputLayer)
self.MAP_REGISTRY.removeMapLayer(mem_layer)
outputLayer.loadNamedStyle(self.displayLayerStyleFile)
return outputLayer