Skip to content

Commit 2430af3

Browse files
committed
add additional tools
1 parent 96ee777 commit 2430af3

18 files changed

+595
-18
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PLUGINNAME = datetimetools
22
PLUGINS = "$(HOME)"/AppData/Roaming/QGIS/QGIS3/profiles/default/python/plugins/$(PLUGINNAME)
3-
PY_FILES = datetimetools.py __init__.py conversionDialog.py copyTimezoneTool.py captureCoordinate.py jdcal.py util.py wintz.py settings.py provider.py addtimezone.py copyModeSettings.py
3+
PY_FILES = datetimetools.py __init__.py conversionDialog.py copyTimezoneTool.py captureCoordinate.py jdcal.py util.py wintz.py settings.py provider.py addtimezone.py copyModeSettings.py addastronomical.py
44
EXTRAS = metadata.txt icon.png
55

66
deploy:
@@ -9,4 +9,9 @@ deploy:
99
cp -vf $(EXTRAS) $(PLUGINS)
1010
cp -vrf images $(PLUGINS)
1111
cp -vrf ui $(PLUGINS)
12+
cp -vfr doc $(PLUGINS)
1213
# cp -vrf libs $(PLUGINS)
14+
cp -vf helphead.html index.html
15+
python -m markdown -x extra readme.md >> index.html
16+
echo '</body>' >> index.html
17+
cp -vf index.html $(PLUGINS)/index.html

addastronomical.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import os
2+
from datetime import datetime
3+
from pytz import timezone
4+
import pytz
5+
from astral.sun import sun
6+
import astral
7+
8+
from qgis.core import (
9+
QgsPointXY, QgsFeature, QgsGeometry, QgsField,
10+
QgsProject, QgsWkbTypes, QgsCoordinateTransform)
11+
12+
from qgis.core import (
13+
QgsProcessing,
14+
QgsProcessingAlgorithm,
15+
QgsProcessingException,
16+
QgsProcessingParameterBoolean,
17+
QgsProcessingParameterDateTime,
18+
QgsProcessingParameterFeatureSource,
19+
QgsProcessingParameterFeatureSink)
20+
21+
from qgis.PyQt.QtGui import QIcon
22+
from qgis.PyQt.QtCore import QVariant, QUrl
23+
24+
from .settings import epsg4326, tzf_instance
25+
26+
class AddAstronomicalAlgorithm(QgsProcessingAlgorithm):
27+
"""
28+
Algorithm to time zone attribute.
29+
"""
30+
31+
PrmInputLayer = 'InputLayer'
32+
PrmUseUTC = 'UseUTC'
33+
PrmOutputLayer = 'OutputLayer'
34+
PrmDate = 'Date'
35+
PrmUseISO = 'UseISO'
36+
37+
def initAlgorithm(self, config):
38+
self.addParameter(
39+
QgsProcessingParameterFeatureSource(
40+
self.PrmInputLayer,
41+
'Input point layer',
42+
[QgsProcessing.TypeVectorPoint])
43+
)
44+
self.addParameter(
45+
QgsProcessingParameterBoolean(
46+
self.PrmUseUTC,
47+
'Use UTC for date and time',
48+
True,
49+
optional=True)
50+
)
51+
self.addParameter(
52+
QgsProcessingParameterBoolean(
53+
self.PrmUseISO,
54+
'Use ISO8601 timestamps',
55+
False,
56+
optional=True)
57+
)
58+
self.addParameter(
59+
QgsProcessingParameterDateTime(
60+
self.PrmDate,
61+
'Select date for solar calculations',
62+
type=QgsProcessingParameterDateTime.Date,
63+
optional=False,
64+
)
65+
)
66+
self.addParameter(
67+
QgsProcessingParameterFeatureSink(
68+
self.PrmOutputLayer,
69+
'Output layer')
70+
)
71+
72+
def processAlgorithm(self, parameters, context, feedback):
73+
source = self.parameterAsSource(parameters, self.PrmInputLayer, context)
74+
use_utc = self.parameterAsBool(parameters, self.PrmUseUTC, context)
75+
use_iso = self.parameterAsBool(parameters, self.PrmUseISO, context)
76+
dt = self.parameterAsDateTime(parameters, self.PrmDate, context)
77+
qdate = dt.date()
78+
if not qdate.isValid():
79+
raise QgsProcessingException('Use a proper date and rerun algorithm')
80+
date = datetime(qdate.year(), qdate.month(), qdate.day())
81+
82+
fields = source.fields()
83+
src_crs = source.sourceCrs()
84+
if fields.append(QgsField("dawn", QVariant.String)) is False:
85+
raise QgsProcessingException("Field names must be unique. There is already a field named 'dawn'")
86+
if fields.append(QgsField("sunrise", QVariant.String)) is False:
87+
raise QgsProcessingException("Field names must be unique. There is already a field named 'sunrise'")
88+
if fields.append(QgsField("noon", QVariant.String)) is False:
89+
raise QgsProcessingException("Field names must be unique. There is already a field named 'noon'")
90+
if fields.append(QgsField("sunset", QVariant.String)) is False:
91+
raise QgsProcessingException("Field names must be unique. There is already a field named 'sunset'")
92+
if fields.append(QgsField("dusk", QVariant.String)) is False:
93+
raise QgsProcessingException("Field names must be unique. There is already a field named 'dusk'")
94+
95+
(sink, dest_id) = self.parameterAsSink(
96+
parameters, self.PrmOutputLayer, context, fields,
97+
source.wkbType(), src_crs)
98+
99+
if src_crs != epsg4326:
100+
transform = QgsCoordinateTransform(src_crs, epsg4326, QgsProject.instance())
101+
if use_iso:
102+
if use_utc:
103+
fmt = '%Y-%m-%dT%H:%M:%SZ'
104+
else:
105+
fmt = '%Y-%m-%dT%H:%M:%S%z'
106+
else:
107+
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
108+
tzf = tzf_instance.getTZF()
109+
total = 100.0 / source.featureCount() if source.featureCount() else 0
110+
111+
iterator = source.getFeatures()
112+
for cnt, feature in enumerate(iterator):
113+
if feedback.isCanceled():
114+
break
115+
f = QgsFeature()
116+
f.setGeometry(feature.geometry())
117+
pt = feature.geometry().asPoint()
118+
if src_crs != epsg4326:
119+
pt = transform.transform(pt)
120+
121+
try:
122+
locl = astral.LocationInfo('','','',pt.y(), pt.x())
123+
if use_utc:
124+
s = sun(locl.observer, date=date)
125+
else:
126+
tz_name = tzf.timezone_at(lng=pt.x(), lat=pt.y())
127+
tz = timezone(tz_name)
128+
loc_dt = tz.localize(date)
129+
s = sun(locl.observer, date=date, tzinfo=loc_dt.tzinfo)
130+
dawn = s["dawn"].strftime(fmt)
131+
sunrise = s["sunrise"].strftime(fmt)
132+
noon = s["noon"].strftime(fmt)
133+
sunset = s["sunset"].strftime(fmt)
134+
dusk = s["dusk"].strftime(fmt)
135+
except Exception:
136+
dawn = ""
137+
sunrise = ""
138+
noon = ""
139+
sunset = ""
140+
dusk = ""
141+
f.setAttributes(feature.attributes() + [dawn, sunrise, noon, sunset, dusk])
142+
sink.addFeature(f)
143+
144+
if cnt % 100 == 0:
145+
feedback.setProgress(int(cnt * total))
146+
147+
return {self.PrmOutputLayer: dest_id}
148+
149+
def name(self):
150+
return 'addsunattributes'
151+
152+
def displayName(self):
153+
return 'Add sun attributes'
154+
155+
def icon(self):
156+
return QIcon(os.path.dirname(__file__) + '/images/sun.svg')
157+
158+
def helpUrl(self):
159+
file = os.path.dirname(__file__) + '/index.html'
160+
if not os.path.exists(file):
161+
return ''
162+
return QUrl.fromLocalFile(file).toString(QUrl.FullyEncoded)
163+
164+
def createInstance(self):
165+
return AddAstronomicalAlgorithm()

addtimezone.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def processAlgorithm(self, parameters, context, feedback):
8383
if src_crs != epsg4326:
8484
transform = QgsCoordinateTransform(src_crs, epsg4326, QgsProject.instance())
8585
tzf = tzf_instance.getTZF()
86+
date = datetime(qdate.year(), qdate.month(), qdate.day())
8687
total = 100.0 / source.featureCount() if source.featureCount() else 0
8788

8889
iterator = source.getFeatures()
@@ -103,7 +104,7 @@ def processAlgorithm(self, parameters, context, feedback):
103104
if add_offset:
104105
if msg:
105106
tz = timezone(msg)
106-
loc_dt = tz.localize(datetime(qdate.year(), qdate.month(), qdate.day()))
107+
loc_dt = tz.localize(date)
107108
offset = loc_dt.strftime('%z')
108109
else:
109110
offset = ''
@@ -120,18 +121,12 @@ def processAlgorithm(self, parameters, context, feedback):
120121
def name(self):
121122
return 'addtimezoneattributes'
122123

123-
'''def icon(self):
124-
return QIcon(os.path.join(os.path.dirname(__file__), 'images/rose.png'))'''
124+
def icon(self):
125+
return QIcon(os.path.join(os.path.dirname(__file__), 'images/tzAttributes.svg'))
125126

126127
def displayName(self):
127128
return 'Add time zone attributes'
128129

129-
def group(self):
130-
return 'Tools'
131-
132-
def groupId(self):
133-
return 'tools'
134-
135130
def helpUrl(self):
136131
file = os.path.dirname(__file__) + '/index.html'
137132
if not os.path.exists(file):

datetimetools.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,29 @@ def initGui(self):
4949
# Add Interface for Time Zone Capturing
5050
icon = QIcon(os.path.dirname(__file__) + "/images/tzCapture.svg")
5151
self.copyTZAction = QAction(icon, "Copy/Display Time Zone", self.iface.mainWindow())
52-
self.copyTZAction.setObjectName('latLonToolsCopy')
5352
self.copyTZAction.triggered.connect(self.startTZCapture)
5453
self.copyTZAction.setCheckable(True)
5554
self.toolbar.addAction(self.copyTZAction)
5655
self.iface.addPluginToMenu("Date/Time Tools", self.copyTZAction)
5756

58-
self.addTZAction = QAction('Add time zone attributes', self.iface.mainWindow())
57+
icon = QIcon(os.path.dirname(__file__) + "/images/sun.svg")
58+
self.addSunAction = QAction(icon, 'Add sun attributes', self.iface.mainWindow())
59+
self.addSunAction.triggered.connect(self.addSunAttributes)
60+
self.toolbar.addAction(self.addSunAction)
61+
self.iface.addPluginToMenu('Date/Time Tools', self.addSunAction)
62+
63+
icon = QIcon(os.path.dirname(__file__) + "/images/tzAttributes.svg")
64+
self.addTZAction = QAction(icon, 'Add time zone attributes', self.iface.mainWindow())
5965
self.addTZAction.triggered.connect(self.addTimeZoneAttributes)
66+
self.toolbar.addAction(self.addTZAction)
6067
self.iface.addPluginToMenu('Date/Time Tools', self.addTZAction)
6168

69+
# Help
70+
icon = QIcon(os.path.dirname(__file__) + '/images/help.svg')
71+
self.helpAction = QAction(icon, "Help", self.iface.mainWindow())
72+
self.helpAction.triggered.connect(self.help)
73+
self.iface.addPluginToMenu('Date/Time Tools', self.helpAction)
74+
6275
self.canvas.mapToolSet.connect(self.resetTools)
6376

6477
# Add the processing provider
@@ -70,7 +83,10 @@ def unload(self):
7083
self.iface.removeToolBarIcon(self.conversionAction)
7184
self.iface.removePluginMenu('Date/Time Tools', self.copyTZAction)
7285
self.iface.removeToolBarIcon(self.copyTZAction)
86+
self.iface.removePluginMenu('Date/Time Tools', self.addSunAction)
87+
self.iface.removeToolBarIcon(self.addSunAction)
7388
self.iface.removePluginMenu('Date/Time Tools', self.addTZAction)
89+
self.iface.removeToolBarIcon(self.addTZAction)
7490
del self.toolbar
7591
if self.conversionDialog:
7692
self.iface.removeDockWidget(self.conversionDialog)
@@ -91,6 +107,9 @@ def resetTools(self, newtool, oldtool):
91107
except Exception:
92108
pass
93109

110+
def addSunAttributes(self):
111+
processing.execAlgorithmDialog('datetimetools:addsunattributes', {})
112+
94113
def addTimeZoneAttributes(self):
95114
processing.execAlgorithmDialog('datetimetools:addtimezoneattributes', {})
96115

@@ -113,4 +132,10 @@ def startTZCapture(self):
113132
self.captureTzTool = CopyTimeZoneTool(self.copyModeSettings, self.iface)
114133
self.canvas.setMapTool(self.captureTzTool)
115134

135+
def help(self):
136+
'''Display a help page'''
137+
import webbrowser
138+
url = QUrl.fromLocalFile(os.path.dirname(__file__) + "/index.html").toString()
139+
webbrowser.open(url, new=2)
140+
116141

doc/add_sun_attributes.png

16.2 KB
Loading

doc/add_tz.png

17 KB
Loading

doc/sun_attributes.png

15.3 KB
Loading

doc/timezonecapture.png

140 KB
Loading

doc/tz_attributes.png

3.72 KB
Loading

helphead.html

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<head>
2+
<style>
3+
body {
4+
font:13.34px helvetica,arial,freesans,clean,sans-serif;
5+
color:black;
6+
line-height:1.4em;
7+
background-color: #F8F8F8;
8+
padding: 0.7em;
9+
}
10+
pre {
11+
margin:1em 0;
12+
font-size:12px;
13+
background-color:#eee;
14+
border:1px solid #ddd;
15+
padding:5px;
16+
line-height:1.5em;
17+
color:#444;
18+
overflow:auto;
19+
-webkit-box-shadow:rgba(0,0,0,0.07) 0 1px 2px inset;
20+
-webkit-border-radius:3px;
21+
-moz-border-radius:3px;border-radius:3px;
22+
}
23+
pre code {
24+
padding:0;
25+
font-size:12px;
26+
background-color:#eee;
27+
border:none;
28+
}
29+
code {
30+
font-size:12px;
31+
background-color:#f8f8ff;
32+
color:#444;
33+
padding:0 .2em;
34+
border:1px solid #dedede;
35+
}
36+
</style>
37+
</head>
38+
<body>

0 commit comments

Comments
 (0)