Skip to content

Commit

Permalink
Merge pull request #15 from ludohenin/custom-step-length
Browse files Browse the repository at this point in the history
Custom step duration
  • Loading branch information
dakk authored Nov 19, 2024
2 parents a4fb0fa + 272993f commit c56ff4a
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 26 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ Calculate subsequent steps until the end track point is reached

```python
while not self.routing_obj.end:
res = self.routing_obj.step()
res = self.routing_obj.step() # default step duration is set to 1 hour

# you can call a step with custom timedelta (in hour) at anytime
while not self.routing_obj.end:
res = self.routing_obj.step(timedelta=0.25) # 15min time delta
```
the step method returns a RoutingResult object with the following informations during routing calculation:
```python
Expand Down
28 changes: 27 additions & 1 deletion tests/linearbestisorouter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def test_step(self):
res = self.routing_obj.step()
i += 1

self.assertEqual(i, 9)
self.assertEqual(i, 10)
self.assertEqual(not res.path, False)


Expand Down Expand Up @@ -254,3 +254,29 @@ def test_step(self):

self.assertEqual(i, 5)
self.assertEqual(not res.path, False)


class TestRouting_custom_step(unittest.TestCase):
def setUp(self):
grib = mock_grib(2, 180, 0.1)
self.track = [(5, 38), (5.2, 38.2)]
island_route = mock_point_validity(self.track, factor=5)
self.routing_obj = weatherrouting.Routing(
LinearBestIsoRouter,
polar_bavaria38,
self.track,
grib,
datetime.datetime.fromisoformat("2021-04-02T12:00:00"),
pointValidity=island_route.point_validity,
)

def test_step(self):
res = None
i = 0

while not self.routing_obj.end:
res = self.routing_obj.step(timedelta=0.5)
i += 1

self.assertEqual(i, 14)
self.assertEqual(not res.path, False)
1 change: 0 additions & 1 deletion tests/mock_grib.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

# For detail about GNU see <http://www.gnu.org/licenses/>.
import datetime
import math
import random


Expand Down
34 changes: 34 additions & 0 deletions tests/shortestpathrouter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,37 @@ def test_step(self):

self.assertEqual(i, 4)
self.assertEqual(not res.path, False)


class TestRouting_custom_step(unittest.TestCase):
def setUp(self):
grib = mock_grib(2, 180, 0.1)
self.track = [(5, 38), (5.2, 38.2)]
island_route = mock_point_validity(self.track)
self.routing_obj = weatherrouting.Routing(
ShortestPathRouter,
None,
self.track,
grib,
datetime.datetime.fromisoformat("2021-04-02T12:00:00"),
pointValidity=island_route.point_validity,
)

def test_step(self):
res = None
i = 0

while not self.routing_obj.end:
res = self.routing_obj.step(timedelta=0.5)
i += 1

self.assertEqual(i, 5)
self.assertEqual(not res.path, False)

path_to_end = res.path + [IsoPoint(self.track[-1])]
self.assertEqual(
res.time, datetime.datetime.fromisoformat("2021-04-02 14:00:00")
)
self.assertEqual(
len(json.dumps(weatherrouting.utils.pathAsGeojson(path_to_end))), 1785
)
24 changes: 16 additions & 8 deletions weatherrouting/routers/linearbestisorouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class LinearBestIsoRouter(Router):
)
}

def _route(self, lastlog, time, start, end, isoF): # noqa: C901
def _route(self, lastlog, time, timedelta, start, end, isoF): # noqa: C901
position = start
path = []

Expand All @@ -50,13 +50,21 @@ def generate_path(p):
path = path[::-1]
position = path[-1].pos

if self.grib.getWindAt(time + datetime.timedelta(hours=1), end[0], end[1]):
if self.grib.getWindAt(
time + datetime.timedelta(hours=timedelta), end[0], end[1]
):
if lastlog is not None and len(lastlog.isochrones) > 0:
isoc = isoF(time + datetime.timedelta(hours=1), lastlog.isochrones, end)
isoc = isoF(
time + datetime.timedelta(hours=timedelta),
timedelta,
lastlog.isochrones,
end,
)
else:
nwdist = utils.pointDistance(end[0], end[1], start[0], start[1])
isoc = isoF(
time + datetime.timedelta(hours=1),
time + datetime.timedelta(hours=timedelta),
timedelta,
[[IsoPoint((start[0], start[1]), time=time, nextWPDist=nwdist)]],
end,
)
Expand All @@ -66,7 +74,7 @@ def generate_path(p):
for p in isoc[-1]:
distance_to_end_point = p.pointDistance(end)
if distance_to_end_point < self.getParamValue("minIncrease"):
# (twd,tws) = self.grib.getWindAt (time + datetime.timedelta(hours=1),
# (twd,tws) = self.grib.getWindAt (time + datetime.timedelta(hours=timedelta),
# p.pos[0], p.pos[1])
maxReachDistance = utils.maxReachDistance(p.pos, p.speed)
if distance_to_end_point < abs(maxReachDistance * 1.1):
Expand Down Expand Up @@ -94,11 +102,11 @@ def generate_path(p):
generate_path(minP)

return RoutingResult(
time=time + datetime.timedelta(hours=1),
time=time + datetime.timedelta(hours=timedelta),
path=path,
position=position,
isochrones=isoc,
)

def route(self, lastlog, t, start, end) -> RoutingResult:
return self._route(lastlog, t, start, end, self.calculateIsochrones)
def route(self, lastlog, t, timedelta, start, end) -> RoutingResult:
return self._route(lastlog, t, timedelta, start, end, self.calculateIsochrones)
16 changes: 7 additions & 9 deletions weatherrouting/routers/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def setParamValue(self, code, value):
def getParamValue(self, code):
return self.PARAMS[code].value

def calculateShortestPathIsochrones(self, fixedSpeed, t, isocrone, nextwp):
def calculateShortestPathIsochrones(self, fixedSpeed, t, dt, isocrone, nextwp):
"""Calculates isochrones based on shortest path at fixed speed (motoring);
the speed considers reductions / increases derived from leeway"""

Expand All @@ -167,9 +167,9 @@ def pointF(p, tws, twa, dt, brg):
speed,
)

return self._calculateIsochronesConcurrent(t, isocrone, nextwp, pointF)
return self._calculateIsochronesConcurrent(t, dt, isocrone, nextwp, pointF)

def calculateIsochrones(self, t, isocrone, nextwp):
def calculateIsochrones(self, t, dt, isocrone, nextwp):
"""Calculate isochrones depending on routageSpeed from polar"""

def pointF(p, tws, twa, dt, brg):
Expand All @@ -184,7 +184,7 @@ def pointF(p, tws, twa, dt, brg):
# math.degrees(brg), 'rpd', rpd)
return rpd

return self._calculateIsochronesConcurrent(t, isocrone, nextwp, pointF)
return self._calculateIsochronesConcurrent(t, dt, isocrone, nextwp, pointF)

def _filterValidity(self, isonew, last): # noqa: C901
def validPoint(a):
Expand Down Expand Up @@ -232,9 +232,8 @@ def validLine(a):

return isonew

def _calculateIsochronesConcurrent(self, t, isocrone, nextwp, pointF):
def _calculateIsochronesConcurrent(self, t, dt, isocrone, nextwp, pointF):
"""Calcuates isochrones based on pointF next point calculation"""
dt = 1.0 / 60.0 * 60.0
last = isocrone[-1]

newisopoints = []
Expand Down Expand Up @@ -313,9 +312,8 @@ def cisopoints(i):

return isocrone

def _calculateIsochrones(self, t, isocrone, nextwp, pointF):
def _calculateIsochrones(self, t, dt, isocrone, nextwp, pointF):
"""Calcuates isochrones based on pointF next point calculation"""
dt = 1.0 / 60.0 * 60.0
last = isocrone[-1]

newisopoints = []
Expand Down Expand Up @@ -390,5 +388,5 @@ def calculateVMG(self, speed, angle, start, end) -> float:
at current speed / angle"""
return speed * math.cos(angle)

def route(self, lastlog, t, start, end) -> RoutingResult:
def route(self, lastlog, t, timedelta, start, end) -> RoutingResult:
raise Exception("Not implemented")
7 changes: 4 additions & 3 deletions weatherrouting/routers/shortestpathrouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ class ShortestPathRouter(LinearBestIsoRouter):
),
}

def route(self, lastlog, t, start, end) -> RoutingResult:
def route(self, lastlog, t, timedelta, start, end) -> RoutingResult:
return self._route(
lastlog,
t,
timedelta,
start,
end,
lambda t, isoc, end: self.calculateShortestPathIsochrones(
self.getParamValue("fixedSpeed"), t, isoc, end
lambda t, dt, isoc, end: self.calculateShortestPathIsochrones(
self.getParamValue("fixedSpeed"), t, timedelta, isoc, end
),
)
10 changes: 7 additions & 3 deletions weatherrouting/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def __init__(
self.wp = 1
self.position = self.track[0]

def step(self) -> RoutingResult:
def step(self, timedelta=1) -> RoutingResult:
"""Execute a single routing step"""
self.steps += 1

Expand All @@ -113,10 +113,14 @@ def step(self) -> RoutingResult:
nextwp = self.track[self.wp]

if self._startingNewPoint or len(self.log) == 0:
res = self.algorithm.route(None, self.time, self.position, nextwp)
res = self.algorithm.route(
None, self.time, timedelta, self.position, nextwp
)
self._startingNewPoint = False
else:
res = self.algorithm.route(self.log[-1], self.time, self.position, nextwp)
res = self.algorithm.route(
self.log[-1], self.time, timedelta, self.position, nextwp
)

# self.time += 0.2
ff = 100 / len(self.track)
Expand Down

0 comments on commit c56ff4a

Please sign in to comment.