CoastalME (Coastal Modelling Environment)
Simulates the long-term behaviour of complex coastlines
Loading...
Searching...
No Matches
calc_shadow_zones.cpp
Go to the documentation of this file.
1
12
13/* ==============================================================================================================================
14
15 This file is part of CoastalME, the Coastal Modelling Environment.
16
17 CoastalME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23==============================================================================================================================*/
24#include <assert.h>
25#include <cmath>
26
27#include <iostream>
28using std::cout;
29using std::endl;
30
31#include <stack>
32using std::stack;
33
34#include <deque>
35using std::deque;
36
37#include <numeric>
38using std::accumulate;
39
40#include "cme.h"
41#include "coast.h"
42#include "simulation.h"
43#include "raster_grid.h"
44
45//===============================================================================================================================
47//===============================================================================================================================
48bool CSimulation::bOnOrOffShoreAndUpOrDownCoast(double const dCoastAngle, double const dWaveAngle, int const nSeaHand, bool& bDownCoast)
49{
50 bool bOnShore;
51 double dWaveToCoastAngle = fmod((dWaveAngle - dCoastAngle + 360), 360);
52
53 bDownCoast = ((dWaveToCoastAngle > 270) || (dWaveToCoastAngle < 90)) ? true : false;
54
55 if (nSeaHand == RIGHT_HANDED)
56 {
57 // The sea is on the RHS when travelling down-coast (i.e. along the coast in the direction of increasing coastline point numbers)
58 bOnShore = dWaveToCoastAngle > 180 ? true : false;
59 }
60
61 else
62 {
63 // The sea is on the LHS when travelling down-coast (i.e. along the coast in the direction of increasing coastline point numbers)
64 bOnShore = dWaveToCoastAngle > 180 ? false : true;
65 }
66
67 return bOnShore;
68}
69
70//===============================================================================================================================
72//===============================================================================================================================
73CGeom2DIPoint CSimulation::PtiFollowWaveAngle(CGeom2DIPoint const* pPtiLast, double const dWaveAngleIn, double &dCorrection)
74{
75 int nXLast = pPtiLast->nGetX();
76 int nYLast = pPtiLast->nGetY();
77 int nXNext = nXLast;
78 int nYNext = nYLast;
79
80 double dWaveAngle = dWaveAngleIn - dCorrection;
81
82 if (dWaveAngle < 22.5)
83 {
84 nYNext--;
85 dCorrection = 22.5 - dWaveAngle;
86 }
87
88 else if (dWaveAngle < 67.5)
89 {
90 nYNext--;
91 nXNext++;
92 dCorrection = 67.5 - dWaveAngle;
93 }
94
95 else if (dWaveAngle < 112.5)
96 {
97 nXNext++;
98 dCorrection = 112.5 - dWaveAngle;
99 }
100
101 else if (dWaveAngle < 157.5)
102 {
103 nXNext++;
104 nYNext++;
105 dCorrection = 157.5 - dWaveAngle;
106 }
107
108 else if (dWaveAngle < 202.5)
109 {
110 nYNext++;
111 dCorrection = 202.5 - dWaveAngle;
112 }
113
114 else if (dWaveAngle < 247.5)
115 {
116 nXNext--;
117 nYNext++;
118 dCorrection = 247.5 - dWaveAngle;
119 }
120
121 else if (dWaveAngle < 292.5)
122 {
123 nXNext--;
124 dCorrection = 292.5 - dWaveAngle;
125 }
126
127 else if (dWaveAngle < 337.5)
128 {
129 nXNext--;
130 nYNext--;
131 dCorrection = 337.5 - dWaveAngle;
132 }
133
134 else
135 {
136 nYNext--;
137 dCorrection = 22.5 - dWaveAngle;
138 }
139
140 dCorrection = dKeepWithin360(dCorrection);
141
142 return CGeom2DIPoint(nXNext, nYNext);
143}
144
145//===============================================================================================================================
147//===============================================================================================================================
149{
151 LogStream << m_ulIter << ": Finding shadow zones" << endl;
152
153 // Do this once for each coastline
154 for (int nCoast = 0; nCoast < static_cast<int>(m_VCoast.size()); nCoast++)
155 {
156 // =========================================================================================================================
157 // The first stage: find coastline start points for possible shadow zone boundaries by sweeping the coastline: first down-coast then up-coast
158 int nSeaHand = m_VCoast[nCoast].nGetSeaHandedness();
159 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
160
161 vector<int> VnPossibleShadowBoundaryCoastPoint;
162
163 for (bool bDownCoast :
164 {
165 true, false
166 })
167 {
168 if (bDownCoast)
169 {
170 bool bLastDownCoastAndOnshore = false;
171
172 // Work along coast in down-coast direction
173 for (int nShadowZoneBoundaryEndPoint = 0; nShadowZoneBoundaryEndPoint < nCoastSize; nShadowZoneBoundaryEndPoint++)
174 {
175 // Get the coast's smoothed curvature at this point
176 double dCurvature = m_VCoast[nCoast].dGetSmoothCurvature(nShadowZoneBoundaryEndPoint);
177
178 if (dCurvature < 0)
179 {
180 // OK, the coast is convex here, now get the flux orientation (a tangent to the coastline)
181 double dFluxOrientation = m_VCoast[nCoast].dGetFluxOrientation(nShadowZoneBoundaryEndPoint);
182
183 // If this coast point is in the active zone, use the breaking wave orientation, otherwise use the deep water wave orientation
184 double dWaveAngle;
185
186 if (bFPIsEqual(m_VCoast[nCoast].dGetDepthOfBreaking(nShadowZoneBoundaryEndPoint), DBL_NODATA, TOLERANCE))
187 // Not in active zone
188 dWaveAngle = m_VCoast[nCoast].dGetCoastDeepWaterWaveAngle(nShadowZoneBoundaryEndPoint);
189
190 else
191 // In active zone
192 dWaveAngle = m_VCoast[nCoast].dGetBreakingWaveAngle(nShadowZoneBoundaryEndPoint);
193
194 // At this point on the coast, are waves on- or off-shore, and up- or down-coast?
195 bDownCoast = false;
196 bool bOnShore = bOnOrOffShoreAndUpOrDownCoast(dFluxOrientation, dWaveAngle, nSeaHand, bDownCoast);
197
198 // CGeom2DPoint PtTmp1 = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nShadowZoneBoundaryEndPoint);
199 // LogStream << m_ulIter << ": going down-coast, coast point (" << nShadowZoneBoundaryEndPoint << ") at {" << PtTmp1.dGetX() << ", " << PtTmp1.dGetY() << "} has " << (bDownCoast ? "down-coast " : "up-coast ") << (bOnShore ? "on-shore" : "off-shore") << " waves, dWaveAngle = " << dWaveAngle << " dFluxOrientation = " << dFluxOrientation << endl;
200
201 if (bDownCoast && (! bOnShore))
202 {
203 // Waves are down-coast and off-shore
204 // CGeom2DPoint PtTmp1 = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nShadowZoneBoundaryEndPoint);
205 // LogStream << m_ulIter << ": going down-coast, waves have off-shore and down-coast component at coast point (" << nShadowZoneBoundaryEndPoint << ") at {" << PtTmp1.dGetX() << ", " << PtTmp1.dGetY() << "}" << endl;
206
207 // If the previous coast point had waves which were down-coast and on-shore, then this could be the boundary of a shadow zone
208 if (bLastDownCoastAndOnshore)
209 {
210 VnPossibleShadowBoundaryCoastPoint.push_back(nShadowZoneBoundaryEndPoint);
211 bLastDownCoastAndOnshore = false;
212
213 // CGeom2DPoint PtTmp = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nShadowZoneBoundaryEndPoint);
214
215 // if (m_nLogFileDetail >= LOG_FILE_ALL)
216 // LogStream << m_ulIter << ": Coast " << nCoast << " has possible shadow boundary start at {" << PtTmp.dGetX() << ", " << PtTmp.dGetY() << "}. Found while going down-coast, this is coast point " << nShadowZoneBoundaryEndPoint << endl;
217 }
218 }
219
220 else if (bDownCoast && bOnShore)
221 {
222 bLastDownCoastAndOnshore = true;
223 }
224
225 else
226 {
227 bLastDownCoastAndOnshore = false;
228 }
229 }
230 }
231 }
232
233 else
234 {
235 // Moving up-coast
236 bool bLastUpCoastAndOnshore = false;
237
238 // Work along coast in up-coast direction
239 for (int nShadowZoneBoundaryEndPoint = nCoastSize - 1; nShadowZoneBoundaryEndPoint >= 0; nShadowZoneBoundaryEndPoint--)
240 {
241 // Get the coast's smoothed curvature at this point
242 double dCurvature = m_VCoast[nCoast].dGetSmoothCurvature(nShadowZoneBoundaryEndPoint);
243
244 if (dCurvature < 0)
245 {
246 // OK, the coast is convex here, now get the flux orientation (a tangent to the coastline)
247 double dFluxOrientation = m_VCoast[nCoast].dGetFluxOrientation(nShadowZoneBoundaryEndPoint);
248
249 // If this coast point is in the active zone, use the breaking wave orientation, otherwise use the deep water wave orientation
250 double dWaveAngle;
251
252 if (bFPIsEqual(m_VCoast[nCoast].dGetDepthOfBreaking(nShadowZoneBoundaryEndPoint), DBL_NODATA, TOLERANCE))
253 // Not in active zone
254 dWaveAngle = m_VCoast[nCoast].dGetCoastDeepWaterWaveAngle(nShadowZoneBoundaryEndPoint);
255
256 else
257 // In active zone
258 dWaveAngle = m_VCoast[nCoast].dGetBreakingWaveAngle(nShadowZoneBoundaryEndPoint);
259
260 // At this point on the coast, are waves on- or off-shore, and up- or down-coast?
261 bDownCoast = false;
262 bool bOnShore = bOnOrOffShoreAndUpOrDownCoast(dFluxOrientation, dWaveAngle, nSeaHand, bDownCoast);
263
264 // CGeom2DPoint PtTmp1 = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nShadowZoneBoundaryEndPoint);
265 // LogStream << m_ulIter << ": going up-coast, coast point (" << nShadowZoneBoundaryEndPoint << ") at {" << PtTmp1.dGetX() << ", " << PtTmp1.dGetY() << "} has " << (bDownCoast ? "down-coast " : "up-coast ") << (bOnShore ? "on-shore" : "off-shore") << " waves, dWaveAngle = " << dWaveAngle << " dFluxOrientation = " << dFluxOrientation << endl;
266
267 if ((! bDownCoast) && (! bOnShore))
268 {
269 // Waves are up-coast and off-shore
270 // CGeom2DPoint PtTmp1 = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nShadowZoneBoundaryEndPoint);
271 // LogStream << m_ulIter << ": going up-coast, waves have off-shore and down-coast component at coast point (" << nShadowZoneBoundaryEndPoint << ") at {" << PtTmp1.dGetX() << ", " << PtTmp1.dGetY() << "}" << endl;
272
273 // If the previous coast point had waves which were up-coast and on-shore, then this could be the boundary of a shadow zone
274 if (bLastUpCoastAndOnshore)
275 {
276 VnPossibleShadowBoundaryCoastPoint.push_back(nShadowZoneBoundaryEndPoint);
277 bLastUpCoastAndOnshore = false;
278
279 // CGeom2DPoint PtTmp = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nShadowZoneBoundaryEndPoint);
280
281 // if (m_nLogFileDetail >= LOG_FILE_ALL)
282 // LogStream << m_ulIter << ": Coast " << nCoast << " has possible shadow boundary start at {" << PtTmp.dGetX() << ", " << PtTmp.dGetY() << "}. Found while going up-coast, this is coast point " << nShadowZoneBoundaryEndPoint << endl;
283 }
284 }
285
286 else if ((! bDownCoast) && bOnShore)
287 {
288 bLastUpCoastAndOnshore = true;
289 }
290
291 else
292 {
293 bLastUpCoastAndOnshore = false;
294 }
295 }
296 }
297 }
298 }
299
300 if (VnPossibleShadowBoundaryCoastPoint.size() == 0)
301 {
303 LogStream << m_ulIter << ": No shadow boundary start points found" << endl;
304
305 return RTN_OK;
306 }
307
308 // =========================================================================================================================
309 // The second stage: we have a list of possible shadow zone start points, trace each of these 'up-wave' to identify valid shadow zones
310 vector<CGeomILine> VILShadowBoundary;
311 vector<int> VnShadowBoundaryStartCoastPoint;
312 vector<int> VnShadowBoundaryEndCoastPoint;
313
314 for (int nStartPoint = 0; nStartPoint < static_cast<int>(VnPossibleShadowBoundaryCoastPoint.size()); nStartPoint++)
315 {
316 // if (m_nLogFileDetail >= LOG_FILE_ALL)
317 // LogStream << m_ulIter << ": Coast " << nCoast << " processing possible shadow boundary start point " << nStartPoint << " of " << VnPossibleShadowBoundaryCoastPoint.size() << endl;
318
319 bool bHitEdge = false;
320 bool bHitCoast = false;
321 bool bHitSea = false;
322 bool bInLoop = false;
323 bool bStillInland = false;
324
325 // From each start point, follow the wave direction
326 CGeomILine ILShadowBoundary;
327
328 // If this coast point is in the active zone, start with the breaking wave orientation, otherwise use the deep water wave orientation
329 double dPrevWaveAngle;
330
331 if (bFPIsEqual(m_VCoast[nCoast].dGetDepthOfBreaking(nStartPoint), DBL_NODATA, TOLERANCE))
332 {
333 // Not in active zone
334 dPrevWaveAngle = m_VCoast[nCoast].dGetCoastDeepWaterWaveAngle(nStartPoint);
335 }
336
337 else
338 {
339 // In active zone
340 dPrevWaveAngle = m_VCoast[nCoast].dGetBreakingWaveAngle(nStartPoint);
341 }
342
343 CGeom2DIPoint PtiPrev = * m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(VnPossibleShadowBoundaryCoastPoint[nStartPoint]);
344 ILShadowBoundary.Append(&PtiPrev);
345
346 int nDist = 0;
347 double dCorrection = 0;
348 deque<double> DQdPrevOrientations;
349
350 while ((! bHitEdge) && (! bHitCoast))
351 {
352 // int nShadowBoundaryCoastPoint = -1;
353
354 if (nDist > 0)
355 {
356 int nXPrev = PtiPrev.nGetX();
357 int nYPrev = PtiPrev.nGetY();
358
359 if (! m_pRasterGrid->m_Cell[nXPrev][nYPrev].bIsInActiveZone())
360 {
361 // The previous cell was outside the active zone, so use its wave orientation value
362 dPrevWaveAngle = m_pRasterGrid->m_Cell[nXPrev][nYPrev].dGetWaveAngle();
363 }
364
365 else
366 {
367 // The previous cell was in the active zone
368 if (bHitSea)
369 {
370 // If this shadow boundary has already hit sea, then we must be getting near a coast: use the average-so-far wave orientation
371 double dAvgOrientationSoFar = accumulate(DQdPrevOrientations.begin(), DQdPrevOrientations.end(), 0.0) / static_cast<double>(DQdPrevOrientations.size());
372
373 dPrevWaveAngle = dAvgOrientationSoFar;
374 }
375
376 else
377 {
378 // This shadow boundary has not already hit sea, just use the wave orientation from the previous cell
379 dPrevWaveAngle = m_pRasterGrid->m_Cell[nXPrev][nYPrev].dGetWaveAngle();
380
381 // LogStream << m_ulIter << ": not already hit sea, using previous cell's wave orientation for cell [" << nXPrev << "][" << nYPrev << "] = {" << dGridCentroidXToExtCRSX(nXPrev) << ", " << dGridCentroidYToExtCRSY(nYPrev) << "}" << endl;
382 }
383 }
384
385 if (bFPIsEqual(dPrevWaveAngle, DBL_NODATA, TOLERANCE))
386 {
387 // LogStream << m_ulIter << ": dPrevWaveAngle == DBL_NODATA for cell [" << nXPrev << "][" << nYPrev << "] = {" << dGridCentroidXToExtCRSX(nXPrev) << ", " << dGridCentroidYToExtCRSY(nYPrev) << "}" << endl;
388
389 if (! m_pRasterGrid->m_Cell[nXPrev][nYPrev].bIsInContiguousSea())
390 {
391 // The previous cell was an inland cell, so use the deep water wave orientation
392 dPrevWaveAngle = m_pRasterGrid->m_Cell[nXPrev][nYPrev].dGetCellDeepWaterWaveAngle();
393 }
394
395 else
396 {
397 double dAvgOrientationSoFar = accumulate(DQdPrevOrientations.begin(), DQdPrevOrientations.end(), 0.0) / static_cast<double>(DQdPrevOrientations.size());
398
399 dPrevWaveAngle = dAvgOrientationSoFar;
400 }
401 }
402
403 if (DQdPrevOrientations.size() == MAX_NUM_PREV_ORIENTATION_VALUES)
404 DQdPrevOrientations.pop_front();
405
406 DQdPrevOrientations.push_back(dPrevWaveAngle);
407 }
408
409 // Go upwave along the previous cell's wave orientation to find the new boundary cell
410 CGeom2DIPoint PtiNew = PtiFollowWaveAngle(&PtiPrev, dPrevWaveAngle, dCorrection);
411
412 // Get the coordinates of 'this' cell
413 int nX = PtiNew.nGetX();
414 int nY = PtiNew.nGetY();
415
416 // Have we hit the edge of the valid part of the grid?
417 if ((! bIsWithinValidGrid(&PtiNew)) || (m_pRasterGrid->m_Cell[nX][nY].bIsBoundingBoxEdge()))
418 {
419 // Yes we have
420 bHitEdge = true;
421
422 // LogStream << m_ulIter << ": shadow boundary " << nStartPoint << " hit edge cell at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
423
424 continue;
425 }
426
427 // Have we been to this cell before?
428 if (ILShadowBoundary.bIsPresent(nX, nY))
429 {
430 // We have, so we are in a loop. Abandon this shadow line
431 bInLoop = true;
432 break;
433 }
434
435 // OK so far. Have we hit a sea cell yet?
436 if ((nDist > MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE) && (! bHitSea))
437 {
438 if (m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea())
439 bHitSea = true;
440
441 else
442 {
444 {
445 // If we have travelled MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE cells without hitting sea, then abandon this shadow boundary
446 bStillInland = true;
447 break;
448 }
449 }
450 }
451
452 // Store the coordinates of every cell which we cross
453 ILShadowBoundary.Append(&PtiNew);
454
455 // LogStream << m_ulIter << ": at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
456
457 // Having hit sea, have we now hit we hit a coast point? Note that two diagonal(ish) raster lines can cross each other without any intersection, so must also test an adjacent cell for intersection (does not matter which adjacent cell)
458 if (bHitSea)
459 {
460 if (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline() || (bIsWithinValidGrid(nX, nY + 1) && m_pRasterGrid->m_Cell[nX][nY + 1].bIsCoastline()))
461 {
462 bHitCoast = true;
463
465 LogStream << m_ulIter << ": Coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " hit the coast at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
466 }
467 }
468
469 // For next time
470 PtiPrev = PtiNew;
471 nDist++;
472 }
473
474 if (bInLoop)
475 {
476 // Shadow line loops, so abandon it
478 LogStream << m_ulIter << ": Coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " forms a loop, abandoning. It starts at [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary[0].nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary[0].nGetY()) << "} and was abandoned at [" << ILShadowBoundary.Back().nGetX() << "][" << ILShadowBoundary.Back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "}" << endl;
479
480 continue;
481 }
482
483 if (bStillInland)
484 {
485 // Shadow line is still inland after crossing MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE calls
486 // if (m_nLogFileDetail >= LOG_FILE_ALL)
487 // LogStream << m_ulIter << ": Coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " is still inland after crossing " << MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE << " cells, abandoning. It starts at [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary[0].nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary[0].nGetY()) << "} and was abandoned at [" << ILShadowBoundary.Back().nGetX() << "][" << ILShadowBoundary.Back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "}" << endl;
488
489 continue;
490 }
491
492 if (bHitCoast)
493 {
494 // The shadow zone boundary has hit a coast, but is the shadow zone line trivially short?
495 double dShadowLen = dGetDistanceBetween(&ILShadowBoundary[0], &ILShadowBoundary.Back()) * m_dCellSide;
496
497 if (dShadowLen < MIN_LENGTH_OF_SHADOW_ZONE_LINE)
498 {
499 // Too short, so forget about it
501 LogStream << m_ulIter << ": Coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " is too short, has length " << dShadowLen << " m but the minimum length is " << MIN_LENGTH_OF_SHADOW_ZONE_LINE << " m. It starts at [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary[0].nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary[0].nGetY()) << "} and hits coast at [" << ILShadowBoundary.Back().nGetX() << "][" << ILShadowBoundary.Back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "}" << endl;
502
503 continue;
504 }
505
506 // We've found a valid shadow zone. Check the last point in the shadow boundary. Note that occasionally this last cell is not 'above' a cell but is above one of its neighbouring cells is: in which case, replace the last point in the shadow boundary with the coordinates of this neighbouring cell
507 int nShadowBoundaryCoastPoint = m_VCoast[nCoast].nGetCoastPointGivenCell(&ILShadowBoundary.Back());
508
509 if (nShadowBoundaryCoastPoint == INT_NODATA)
510 {
511 // Could not find a neighbouring cell which is 'under' the coastline
513 LogStream << m_ulIter << ": coast " << nCoast << ", no coast point under {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "}" << endl;
514
515 // TODO 004 Need to fix this, for the moment just abandon this shadow zone and carry on
516 continue;
517 // return RTN_ERR_NO_CELL_UNDER_COASTLINE;
518 }
519
520 // Now store the shadow zone boundary information
521 VILShadowBoundary.push_back(ILShadowBoundary);
522 VnShadowBoundaryStartCoastPoint.push_back(VnPossibleShadowBoundaryCoastPoint[nStartPoint]);
523 VnShadowBoundaryEndCoastPoint.push_back(nShadowBoundaryCoastPoint);
524
526 LogStream << m_ulIter << ": Coast " << nCoast << ", coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " defines a valid shadow zone. Start point [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary[0].nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary[0].nGetY()) << "}, hits coast at [" << ILShadowBoundary.Back().nGetX() << "][" << ILShadowBoundary.Back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "} which is coast point (" << nShadowBoundaryCoastPoint << "). Will be shadow zone " << VnShadowBoundaryEndCoastPoint.size() - 1 << endl;
527 }
528
529 if (bHitEdge)
530 {
532 {
533 // We are creating shadow zones if we hit the grid edge. But is the shadow zone line trivially short?
534 double dShadowLen = dGetDistanceBetween(&ILShadowBoundary[0], &ILShadowBoundary.Back()) * m_dCellSide;
535
536 if (dShadowLen < MIN_LENGTH_OF_SHADOW_ZONE_LINE)
537 {
538 // Too short, so forget about it
540 LogStream << m_ulIter << ": Possible shadow boundary from start point " << nStartPoint << " is too short, has length " << dShadowLen << " m but the minimum length is " << MIN_LENGTH_OF_SHADOW_ZONE_LINE << " m. It starts at [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary[0].nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary[0].nGetY()) << "} and hits the grid edge at [" << ILShadowBoundary.Back().nGetX() << "][" << ILShadowBoundary.Back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "}" << endl;
541
542 break;
543 }
544
545 // We've found a valid grid-edge shadow zone, but we need a distance (in cells) between the shadow boundary start and the 'virtual' shadow boundary end: this is the off-grid point where the shadow boundary would have intersected the coastline, if the grid were big enough. This is of course unknowable. So as a best guess, we choose the shorter of the two distances between the point where the shadow boundary hits the valid edge of the grid, and the start or end of the coast
546 // int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
547 CGeom2DIPoint PtiCoastStart = * m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0);
548 CGeom2DIPoint PtiCoastEnd = * m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1);
549
550 int nDistance = nRound(tMin(dGetDistanceBetween(&ILShadowBoundary.Back(), &PtiCoastStart), dGetDistanceBetween(&ILShadowBoundary.Back(), &PtiCoastEnd)));
551
552 // Now store the shadow zone boundary information
553 VILShadowBoundary.push_back(ILShadowBoundary);
554 VnShadowBoundaryStartCoastPoint.push_back(VnPossibleShadowBoundaryCoastPoint[nStartPoint]);
555 VnShadowBoundaryEndCoastPoint.push_back(nDistance);
556
558 LogStream << m_ulIter << ": Coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " defines a valid shadow zone. Start point [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary[0].nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary[0].nGetY()) << "}, hit the grid edge at [" << ILShadowBoundary.Back().nGetX() << "][" << ILShadowBoundary.Back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(ILShadowBoundary.Back().nGetX()) << ", " << dGridCentroidYToExtCRSY(ILShadowBoundary.Back().nGetY()) << "}. Best-guess length of the shadow boundary is " << nDistance << " cells. Will be shadow zone " << VnShadowBoundaryEndCoastPoint.size() - 1 << endl;
559 }
560
561 else
562 {
563 // We are not creating shadow zones if we hit the grid edge
565 LogStream << m_ulIter << ": Coast " << nCoast << ", possible shadow boundary from start point " << nStartPoint << " hits a grid edge: ignored. It starts at [" << ILShadowBoundary[0].nGetX() << "][" << ILShadowBoundary[0].nGetY() << "]" << endl;
566 }
567 }
568 }
569
570 // =========================================================================================================================
571 // The third stage: store the shadow zone boundary, cell-by-cell fill the shadow zone, then change wave properties by sweeping the shadow zone and the area downdrift from the shadow zone
572 for (unsigned int nZone = 0; nZone < VILShadowBoundary.size(); nZone++)
573 {
575 LogStream << m_ulIter << ": coast " << nCoast << ", processing shadow zone " << nZone << " of " << VILShadowBoundary.size() << endl;
576
577 int nShadowLineLen = VILShadowBoundary[nZone].nGetSize();
578
579 // The vector shadow boundary (external CRS)
580 CGeomLine LBoundary;
581
582 // And the same with grid CRS
583 CGeomILine LIBoundary;
584
585 for (int nn = 0; nn < nShadowLineLen; nn++)
586 {
587 int
588 nTmpX = VILShadowBoundary[nZone][nn].nGetX(),
589 nTmpY = VILShadowBoundary[nZone][nn].nGetY();
590
591 // Mark the cells as shadow zone boundary
592 m_pRasterGrid->m_Cell[nTmpX][nTmpY].SetShadowZoneBoundary();
593
594 // If this is a sea cell, mark the shadow zone boundary cell as being in the shadow zone, but not yet processed (a -ve number)
595 if (m_pRasterGrid->m_Cell[nTmpX][nTmpY].bIsInContiguousSea())
596 m_pRasterGrid->m_Cell[nTmpX][nTmpY].SetShadowZoneNumber(-(nZone + 1));
597
598 // If not already there, append this values to the two shadow boundary vectors
600 LIBoundary.AppendIfNotAlready(nTmpX, nTmpY);
601
602 // LogStream << m_ulIter << ": coast " << nCoast << " shadow zone " << nZone << ", which starts at [" << nTmpX << "][" << nTmpY << "] = {" << dGridCentroidXToExtCRSX(nTmpX) << ", " << dGridCentroidYToExtCRSY(nTmpY) << "} has cell [" << nTmpX << "][" << nTmpY << "] marked as shadow zone boundary" << endl;
603 }
604
605 // Put the ext CRS vector shadow boundary into reverse sequence (i.e. start point is last)
606 LBoundary.Reverse();
607
608 // Store the reversed ext CRS shadow zone boundary
609 m_VCoast[nCoast].AppendShadowBoundary(&LBoundary);
610
611 int nStartX = VILShadowBoundary[nZone][0].nGetX();
612 int nStartY = VILShadowBoundary[nZone][0].nGetY();
613 int nEndX = VILShadowBoundary[nZone][nShadowLineLen - 1].nGetX();
614 int nEndY = VILShadowBoundary[nZone][nShadowLineLen - 1].nGetY();
615
616 // Grid CRS
617 CGeom2DIPoint PtiStart(nStartX, nStartY);
618 CGeom2DIPoint PtiEnd(nEndX, nEndY);
619
620 // Cell-by-cell fill the shadow zone: start by finding the centroid
621 if (VnShadowBoundaryEndCoastPoint[nZone] > VnShadowBoundaryStartCoastPoint[nZone])
622 {
623 // The shadow boundary endpoint is down-coast from the shadow boundary start point
624 int nStart = tMax(VnShadowBoundaryStartCoastPoint[nZone], 0);
625 int nEnd = tMin(VnShadowBoundaryEndCoastPoint[nZone], m_VCoast[nCoast].nGetCoastlineSize());
626
627 for (int nn = nStart; nn < nEnd; nn++)
628 {
629 // Append the coastal portion of the shadow zone boundary to the grid CRS vector
630 LIBoundary.Append(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nn));
631 // LogStream << "Coast point A " << nn << " [" << m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nn)->nGetX() << "][" << m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nn)->nGetY() << "]" << endl;
632 }
633 }
634
635 else
636 {
637 // The shadow boundary endpoint is up-coast from the shadow boundary start point
638 int nStart = tMin(VnShadowBoundaryEndCoastPoint[nZone], m_VCoast[nCoast].nGetCoastlineSize() - 1);
639 int nEnd = tMax(VnShadowBoundaryStartCoastPoint[nZone], 0);
640
641 for (int nn = nStart; nn >= nEnd; nn--)
642 {
643 // Append the coastal portion of the shadow zone boundary to the grid CRS vector
644 LIBoundary.Append(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nn));
645 // LogStream << "Coast point B " << nn << " [" << m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nn)->nGetX() << "][" << m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nn)->nGetY() << "]" << endl;
646 }
647 }
648
649 // // DEBUG CODE =======================================================================================================================
650 // LogStream << "FIRST" << endl;
651 // for (int k = 0; k < LIBoundary.nGetSize(); k++)
652 // {
653 // CGeom2DIPoint PtiTmp = *LIBoundary.pPtiGetAt(k);
654 // LogStream << k << " [" << PtiTmp.nGetX() << "][" << PtiTmp.nGetY() << "]" << endl;
655 // }
656 // LogStream << endl;
657 // // DEBUG CODE =======================================================================================================================
658
659 // Calculate the centroid
660 CGeom2DIPoint PtiCentroid = PtiPolygonCentroid(LIBoundary.pPtiVGetPoints());
661
662 if (bIsWithinValidGrid(&PtiCentroid)) // Safety check
663 {
664 int nRet = nFloodFillShadowZone(nZone, &PtiCentroid, &PtiStart, &PtiEnd);
665
666 if (nRet != RTN_OK)
667 {
668 // Could not find start point for cell-by-cell fill. How serious we judge this to be depends on the length of the shadow zone line
669 if (nShadowLineLen < MAX_LEN_SHADOW_LINE_TO_IGNORE)
670 {
672 LogStream << m_ulIter << ": " << WARN << "could not find start point for cell-by-cell fill of shadow zone " << nZone << " but continuing simulation because this is a small shadow zone (shadow line length = " << nShadowLineLen << " cells)" << endl;
673
674 continue;
675 }
676
677 else
678 {
679 LogStream << m_ulIter << ": " << ERR << "could not find start point for cell-by-cell fill of shadow zone " << nZone << " (shadow line length = " << nShadowLineLen << " cells)" << endl;
680 return nRet;
681 }
682 }
683
684 // Sweep the shadow zone, changing wave orientation and height
685 DoShadowZoneAndDownDriftZone(nCoast, nZone, VnShadowBoundaryStartCoastPoint[nZone], VnShadowBoundaryEndCoastPoint[nZone]);
686 }
687 }
688 }
689
690 return RTN_OK;
691}
692
693//===============================================================================================================================
695//===============================================================================================================================
696int CSimulation::nFloodFillShadowZone(int const nZone, CGeom2DIPoint const* pPtiCentroid, CGeom2DIPoint const* pPtiShadowBoundaryStart, CGeom2DIPoint const* pPtiShadowBoundaryEnd)
697{
698 // Is the centroid a sea cell?
699 bool bStartPointOK = true;
700 bool bAllPointNotSea = true;
701 CGeom2DIPoint PtiFloodFillStart = * pPtiCentroid;
702
703 if (! m_pRasterGrid->m_Cell[PtiFloodFillStart.nGetX()][PtiFloodFillStart.nGetY()].bIsInContiguousSea())
704 {
705 // No it isn't: so try to find a cell that is
706 bStartPointOK = false;
707 double dWeight = 0.05;
708
709 while ((! bStartPointOK) && (dWeight < 1))
710 {
711 // Find a start point for the Cell-by-cell fill. Because shadow zones are generally triangular, start by choosing a low weighting so that the start point is close to the centroid, but a bit towards the coast. If this doesn't work, go further coastwards
712 PtiFloodFillStart = PtiWeightedAverage(pPtiShadowBoundaryEnd, pPtiCentroid, dWeight);
713
714 // Safety check
715 if (PtiFloodFillStart == * pPtiCentroid)
716 {
717 dWeight += 0.05;
718 continue;
719 }
720
721 // Safety check
722 if (! bIsWithinValidGrid(&PtiFloodFillStart))
723 {
725 LogStream << m_ulIter << ": " << ERR << "start point [" << PtiFloodFillStart.nGetX() << "][" << PtiFloodFillStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiFloodFillStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiFloodFillStart.nGetY()) << "} for cell-by-cell fill of shadow zone is outside grid" << endl;
726
728 }
729
730 if (m_pRasterGrid->m_Cell[PtiFloodFillStart.nGetX()][PtiFloodFillStart.nGetY()].bIsInContiguousSea())
731 {
732 // Start point is a sea cell, all OK
733 bStartPointOK = true;
734 bAllPointNotSea = false;
735 }
736
737 else
738 {
739 // Start point is not a sea cell
741 LogStream << m_ulIter << ": shadow zone cell-by-cell fill start point [" << PtiFloodFillStart.nGetX() << "][" << PtiFloodFillStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiFloodFillStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiFloodFillStart.nGetY()) << "} is NOT a sea cell for shadow boundary from cape point [" << pPtiShadowBoundaryStart->nGetX() << "][" << pPtiShadowBoundaryStart->nGetY() << "] = {" << dGridCentroidXToExtCRSX(pPtiShadowBoundaryStart->nGetX()) << ", " << dGridCentroidYToExtCRSY(pPtiShadowBoundaryStart->nGetY()) << "} to [" << pPtiShadowBoundaryEnd->nGetX() << "][" << pPtiShadowBoundaryEnd->nGetY() << "] = {" << dGridCentroidXToExtCRSX(pPtiShadowBoundaryEnd->nGetX()) << ", " << dGridCentroidYToExtCRSY(pPtiShadowBoundaryEnd->nGetY()) << "}, dWeight = " << dWeight << endl;
742
743 dWeight += 0.05;
744 }
745 }
746 }
747
748 if ((! bStartPointOK) && (! bAllPointNotSea))
749 {
751 LogStream << m_ulIter << ": " << ERR << "could not find shadow zone cell-by-cell fill start point" << endl;
752
754 }
755
757 LogStream << m_ulIter << ": shadow zone cell-by-cell fill start point [" << PtiFloodFillStart.nGetX() << "][" << PtiFloodFillStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiFloodFillStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiFloodFillStart.nGetY()) << "} is OK for shadow boundary from [" << pPtiShadowBoundaryStart->nGetX() << "][" << pPtiShadowBoundaryStart->nGetY() << "] = {" << dGridCentroidXToExtCRSX(pPtiShadowBoundaryStart->nGetX()) << ", " << dGridCentroidYToExtCRSY(pPtiShadowBoundaryStart->nGetY()) << "} to [" << pPtiShadowBoundaryEnd->nGetX() << "][" << pPtiShadowBoundaryEnd->nGetY() << "] = {" << dGridCentroidXToExtCRSX(pPtiShadowBoundaryEnd->nGetX()) << ", " << dGridCentroidYToExtCRSY(pPtiShadowBoundaryEnd->nGetY()) << "}" << endl;
758
759 // All OK, so create an empty stack
760 stack<CGeom2DIPoint> PtiStack;
761
762 // We have a cell-by-cell fill start point so push this point onto the stack
763 PtiStack.push(PtiFloodFillStart);
764
765 // Then do the cell-by-cell fill: loop until there are no more cell coordinates on the stack
766 while (! PtiStack.empty())
767 {
768 CGeom2DIPoint Pti = PtiStack.top();
769 PtiStack.pop();
770
771 int
772 nX = Pti.nGetX(),
773 nY = Pti.nGetY();
774
775 while ((nX >= 0) && m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea() && (! m_pRasterGrid->m_Cell[nX][nY].bIsinThisShadowZone(-nZone - 1)) && (! m_pRasterGrid->m_Cell[nX][nY].bIsShadowZoneBoundary()) && (! m_pRasterGrid->m_Cell[nX][nY].bIsCoastline()))
776 nX--;
777
778 nX++;
779
780 bool
781 bSpanAbove = false,
782 bSpanBelow = false;
783
784 while ((nX < m_nXGridSize) && m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea() && (! m_pRasterGrid->m_Cell[nX][nY].bIsinThisShadowZone(-nZone - 1)) && (! m_pRasterGrid->m_Cell[nX][nY].bIsShadowZoneBoundary()) && (! m_pRasterGrid->m_Cell[nX][nY].bIsCoastline()))
785 {
786 // Mark the cell as being in the shadow zone but not yet processed (a -ve number, with -1 being zone 1)
787 m_pRasterGrid->m_Cell[nX][nY].SetShadowZoneNumber(-nZone - 1);
788
789 // LogStream << m_ulIter << ": [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} marked as shadow zone" << endl;
790
791 if ((! bSpanAbove) && (nY > 0) && m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea() && (! m_pRasterGrid->m_Cell[nX][nY - 1].bIsinThisShadowZone(-nZone - 1)) && (! m_pRasterGrid->m_Cell[nX][nY - 1].bIsShadowZoneBoundary()) && (! m_pRasterGrid->m_Cell[nX][nY - 1].bIsCoastline()))
792 {
793 PtiStack.push(CGeom2DIPoint(nX, nY - 1));
794 bSpanAbove = true;
795 }
796
797 else if (bSpanAbove && (nY > 0) && ((! m_pRasterGrid->m_Cell[nX][nY - 1].bIsInContiguousSea()) || m_pRasterGrid->m_Cell[nX][nY - 1].bIsinThisShadowZone(-nZone - 1) || m_pRasterGrid->m_Cell[nX][nY - 1].bIsShadowZoneBoundary() || m_pRasterGrid->m_Cell[nX][nY - 1].bIsCoastline()))
798 {
799 bSpanAbove = false;
800 }
801
802 if ((! bSpanBelow) && m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea() && (nY < m_nYGridSize - 1) && (! m_pRasterGrid->m_Cell[nX][nY + 1].bIsinThisShadowZone(-nZone - 1)) && (! m_pRasterGrid->m_Cell[nX][nY + 1].bIsShadowZoneBoundary()) && (! m_pRasterGrid->m_Cell[nX][nY + 1].bIsCoastline()))
803 {
804 PtiStack.push(CGeom2DIPoint(nX, nY + 1));
805 bSpanBelow = true;
806 }
807
808 else if (bSpanBelow && (nY < m_nYGridSize - 1) && ((! m_pRasterGrid->m_Cell[nX][nY + 1].bIsInContiguousSea()) || m_pRasterGrid->m_Cell[nX][nY + 1].bIsinThisShadowZone(-nZone - 1) || m_pRasterGrid->m_Cell[nX][nY + 1].bIsShadowZoneBoundary() || m_pRasterGrid->m_Cell[nX][nY + 1].bIsCoastline()))
809 {
810 bSpanBelow = false;
811 }
812
813 nX++;
814 }
815 }
816
817 return RTN_OK;
818}
819
820//===============================================================================================================================
822//===============================================================================================================================
823void CSimulation::DoShadowZoneAndDownDriftZone(int const nCoast, int const nZone, int const nShadowBoundaryStartPoint, int const nShadowBoundaryEndPoint)
824{
825 int nCoastSeaHand = m_VCoast[nCoast].nGetSeaHandedness();
826 int nShadowZoneCoastToCapeSeaHand;
827
828 if (nCoastSeaHand == LEFT_HANDED)
829 nShadowZoneCoastToCapeSeaHand = RIGHT_HANDED;
830
831 else
832 nShadowZoneCoastToCapeSeaHand = LEFT_HANDED;
833
834 // We will traverse the coastline from the start point of the shadow zone line, going toward the end point. Which direction is this?
835 bool bSweepDownCoast = true;
836
837 if (nShadowBoundaryEndPoint < nShadowBoundaryStartPoint)
838 bSweepDownCoast = false;
839
840 // Get the distance (in cells) from the shadow boundary start point to the shadow boundary end point, going along the coast
841 int nAlongCoastDistanceToShadowEndpoint = tAbs(nShadowBoundaryEndPoint - nShadowBoundaryStartPoint - 1);
842
843 // Calculate the point on the coastline which is 2 * nAlongCoastDistanceToShadowEndpoint from the shadow boundary start point, this will be the end point of the downdrift zone. This point may be beyond the end of the coastline in either direction
844 int nDownDriftEndPoint;
845 int nTotAlongCoastDistanceToDownDriftEndpoint = 2 * nAlongCoastDistanceToShadowEndpoint;
846
847 if (bSweepDownCoast)
848 nDownDriftEndPoint = nShadowBoundaryStartPoint + nTotAlongCoastDistanceToDownDriftEndpoint;
849
850 else
851 nDownDriftEndPoint = nShadowBoundaryStartPoint - nTotAlongCoastDistanceToDownDriftEndpoint;
852
853 // Next find the actual (i.e. within-grid) end of the downdrift line
854 CGeom2DIPoint PtiDownDriftEndPoint;
855
856 // Is the downdrift end point beyond the start or end of the coastline?
857 if (nDownDriftEndPoint < 0)
858 {
859 // Is beyond the start of the coastline
860 int nStartEdge = m_VCoast[nCoast].nGetStartEdge();
861
862 if (nStartEdge == NORTH)
863 {
864 PtiDownDriftEndPoint.SetX(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0)->nGetX());
865 PtiDownDriftEndPoint.SetY(nDownDriftEndPoint);
866 }
867
868 else if (nStartEdge == SOUTH)
869 {
870 PtiDownDriftEndPoint.SetX(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0)->nGetX());
871 PtiDownDriftEndPoint.SetY(m_nYGridSize - nDownDriftEndPoint - 1);
872 }
873
874 else if (nStartEdge == WEST)
875 {
876 PtiDownDriftEndPoint.SetX(nDownDriftEndPoint);
877 PtiDownDriftEndPoint.SetY(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0)->nGetY());
878 }
879
880 else if (nStartEdge == EAST)
881 {
882 PtiDownDriftEndPoint.SetX(m_nXGridSize - nDownDriftEndPoint - 1);
883 PtiDownDriftEndPoint.SetY(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0)->nGetY());
884 }
885 }
886
887 else if (nDownDriftEndPoint >= m_VCoast[nCoast].nGetCoastlineSize())
888 {
889 // Is beyond the end of the coastline
890 int nEndEdge = m_VCoast[nCoast].nGetEndEdge();
891 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
892
893 if (nEndEdge == NORTH)
894 {
895 PtiDownDriftEndPoint.SetX(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1)->nGetX());
896 PtiDownDriftEndPoint.SetY(-nDownDriftEndPoint);
897 }
898
899 else if (nEndEdge == SOUTH)
900 {
901 PtiDownDriftEndPoint.SetX(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1)->nGetX());
902 PtiDownDriftEndPoint.SetY(m_nYGridSize + nDownDriftEndPoint);
903 }
904
905 else if (nEndEdge == WEST)
906 {
907 PtiDownDriftEndPoint.SetX(-nDownDriftEndPoint);
908 PtiDownDriftEndPoint.SetY(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1)->nGetY());
909 }
910
911 else if (nEndEdge == EAST)
912 {
913 PtiDownDriftEndPoint.SetX(m_nXGridSize + nDownDriftEndPoint);
914 PtiDownDriftEndPoint.SetY(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1)->nGetY());
915 }
916 }
917
918 else
919 {
920 // Is on the coastline, so get the location (grid CRS)
921 PtiDownDriftEndPoint = * m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nDownDriftEndPoint);
922 }
923
924 // Get the location (grid CRS) of the shadow boundary start point: this is also the start point of the downdrift boundary
925 CGeom2DIPoint const* pPtiDownDriftBoundaryStartPoint = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nShadowBoundaryStartPoint);
926
927 // Now trace the down-drift boundary line: interpolate between cells by a simple DDA line algorithm, see http://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm) Note that Bresenham's algorithm gave occasional gaps
928 int nXStart = pPtiDownDriftBoundaryStartPoint->nGetX();
929 int nYStart = pPtiDownDriftBoundaryStartPoint->nGetY();
930 int nXEnd = PtiDownDriftEndPoint.nGetX();
931 int nYEnd = PtiDownDriftEndPoint.nGetY();
932
933 // Safety check
934 if ((nXStart == nXEnd) && (nYStart == nYEnd))
935 return;
936
937 double dXStart = dGridCentroidXToExtCRSX(nXStart);
938 double dYStart = dGridCentroidYToExtCRSY(nYStart);
939 double dXEnd = dGridCentroidXToExtCRSX(nXEnd);
940 double dYEnd = dGridCentroidYToExtCRSY(nYEnd);
941 double dXInc = dXEnd - dXStart;
942 double dYInc = dYEnd - dYStart;
943 double dLength = tMax(tAbs(dXInc), tAbs(dYInc));
944
945 dXInc /= dLength;
946 dYInc /= dLength;
947
948 int nTotDownDriftBoundaryDistance = 0;
949 double dX = nXStart;
950 double dY = nYStart;
951
952 CGeomLine LDownDriftBoundary;
953
954 // Process each interpolated point
955 for (int m = 0; m <= nRound(dLength); m++)
956 {
957 int nX = nRound(dX);
958 int nY = nRound(dY);
959
960 if (! bIsWithinValidGrid(nX, nY))
961 {
962 // Safety check
963 break;
964 }
965
966 // OK, this is part of the downdrift boundary so store this coordinate and mark the cell
968
969 // Make sure we have not already stored this coordinate (can happen, due to rounding)
970 if ((LDownDriftBoundary.nGetSize() == 0) || (PtThis != LDownDriftBoundary.pPtBack()))
971 {
972 // Store this coordinate
973 LDownDriftBoundary.Append(&PtThis);
974
975 // Mark the cell (a +ve number, same as the associated shadow zone number i.e. starting from 1)
976 m_pRasterGrid->m_Cell[nX][nY].SetDownDriftZoneNumber(nZone + 1);
977
978 // Increment the boundary length
979 nTotDownDriftBoundaryDistance++;
980
981 // LogStream << "DownDrift boundary [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
982
983 // And increment for next time
984 if (dXEnd > dXStart)
985 dX -= dXInc;
986
987 else
988 dX += dXInc;
989
990 if (dYEnd > dYStart)
991 dY += dYInc;
992
993 else
994 dY -= dYInc;
995 }
996 }
997
998 // Store the downdrift boundary (external CRS), with the start point first
999 m_VCoast[nCoast].AppendShadowDowndriftBoundary(&LDownDriftBoundary);
1000
1001 // Compare the lengths of the along-coast and the along-downdrift boundaries. The increment will be 1 for the smaller of the two, will be > 1 for the larger of the two
1002 int nMaxDistance;
1003 double dAlongCoastIncrement = 1;
1004 double dDownDriftBoundaryIncrement = 1;
1005
1006 if (nTotAlongCoastDistanceToDownDriftEndpoint < nTotDownDriftBoundaryDistance)
1007 {
1008 // The downdrift boundary distance is the larger, so change it
1009 dDownDriftBoundaryIncrement = static_cast<double>(nTotDownDriftBoundaryDistance) / nTotAlongCoastDistanceToDownDriftEndpoint;
1010 nMaxDistance = nTotDownDriftBoundaryDistance;
1011 }
1012
1013 else
1014 {
1015 // The along-coast distance is the larger, so change it
1016 dAlongCoastIncrement = static_cast<double>(nTotAlongCoastDistanceToDownDriftEndpoint) / nTotDownDriftBoundaryDistance;
1017 nMaxDistance = nTotAlongCoastDistanceToDownDriftEndpoint;
1018 }
1019
1020 double dCoastDistSoFar = 0;
1021 double dDownDriftBoundaryDistSoFar = 0;
1022
1023 // Now traverse the along-coast line and the down-drift boundary line, but with different increments for each
1024 for (int n = 1; n < nMaxDistance - 1; n++)
1025 {
1026 dCoastDistSoFar += dAlongCoastIncrement;
1027 dDownDriftBoundaryDistSoFar += dDownDriftBoundaryIncrement;
1028
1029 // LogStream << "dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << endl;
1030
1031 if ((dCoastDistSoFar >= nTotAlongCoastDistanceToDownDriftEndpoint) || (dDownDriftBoundaryDistSoFar >= nTotDownDriftBoundaryDistance))
1032 break;
1033
1034 bool bPastShadowEnd = false;
1035 int nAlongCoast;
1036
1037 if (bSweepDownCoast)
1038 {
1039 nAlongCoast = nShadowBoundaryStartPoint + nRound(dCoastDistSoFar);
1040
1041 if (nAlongCoast >= m_VCoast[nCoast].nGetCoastlineSize())
1042 break;
1043
1044 if (nAlongCoast >= nShadowBoundaryEndPoint)
1045 bPastShadowEnd = true;
1046 }
1047
1048 else
1049 {
1050 nAlongCoast = nShadowBoundaryStartPoint - nRound(dCoastDistSoFar);
1051
1052 if (nAlongCoast < 0)
1053 break;
1054
1055 if (nAlongCoast <= nShadowBoundaryEndPoint)
1056 bPastShadowEnd = true;
1057 }
1058
1059 int nAlongDownDriftBoundary = nRound(dDownDriftBoundaryDistSoFar);
1060
1061 // LogStream << endl << m_ulIter << ": dCoastDistSoFar = " << dCoastDistSoFar << " (nTotAlongCoastDistanceToDownDriftEndpoint = " << nTotAlongCoastDistanceToDownDriftEndpoint << ") dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " (nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << ")" << endl;
1062
1063 // nAlongCoast = " << nAlongCoast << ", nShadowBoundaryEndPoint = " << nShadowBoundaryEndPoint << ", << ", nAlongDownDriftBoundary = " << nAlongDownDriftBoundary << ", << endl;
1064
1065 // Get the two endpoints of the linking line
1066 CGeom2DIPoint const* pPtiCoast = m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nAlongCoast);
1067 int nCoastX = pPtiCoast->nGetX();
1068 int nCoastY = pPtiCoast->nGetY();
1069
1070 int nDownDriftX = nRound(dExtCRSXToGridX(LDownDriftBoundary[nAlongDownDriftBoundary].dGetX()));
1071
1072 // Safety check
1073 if (nCoastX >= m_nXGridSize)
1074 continue;
1075
1076 int nDownDriftY = nRound(dExtCRSYToGridY(LDownDriftBoundary[nAlongDownDriftBoundary].dGetY()));
1077
1078 // Safety check
1079 if (nCoastY >= m_nYGridSize)
1080 continue;
1081
1082 // Safety check, in case the two points are identical (can happen due to rounding)
1083 if ((nCoastX == nDownDriftX) && (nCoastY == nDownDriftY))
1084 {
1086 LogStream << m_ulIter << ": Coast point and downdrift boundary point [" << nCoastX << "][" << nCoastY << "] = {" << dGridCentroidXToExtCRSX(nCoastX) << ", " << dGridCentroidYToExtCRSY(nCoastY) << "} are identical, ignoring" << endl;
1087
1088 continue;
1089 }
1090
1091 // Traverse the linking line, interpolating between cells by a simple DDA line algorithm, see http://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm)
1092 dXInc = nDownDriftX - nCoastX;
1093 dYInc = nDownDriftY - nCoastY;
1094 double dLinkingLineLength = tMax(tAbs(dXInc), tAbs(dYInc));
1095
1096 dXInc /= dLinkingLineLength;
1097 dYInc /= dLinkingLineLength;
1098
1099 dX = nCoastX,
1100 dY = nCoastY;
1101
1102 // Process each interpolated point along the linking line
1103 int nXLast = -1;
1104 int nYLast = -1;
1105 int nShadowZoneLength = 0;
1106 vector<int> VnShadowCellX, VnShadowCellY;
1107
1108 for (int m = 0; m < dLinkingLineLength; m++)
1109 {
1110 int nX = nRound(dX);
1111 int nY = nRound(dY);
1112
1113 // Check to see if we just processed this point, can happen due to rounding
1114 if ((nX == nXLast) && (nY == nYLast))
1115 {
1116 // LogStream << m_ulIter << ": n = " << n << ", m = " << m << ", dLinkingLineLength = " << dLinkingLineLength << ", dCoastDistSoFar = " << dCoastDistSoFar << " (nTotAlongCoastDistanceToDownDriftEndpoint = " << nTotAlongCoastDistanceToDownDriftEndpoint << "), dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " (nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << ") same as last point at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1117
1118 // Set for next time
1119 nXLast = nX;
1120 nYLast = nY;
1121 dX += dXInc;
1122 dY += dYInc;
1123
1124 continue;
1125 }
1126
1127 // Outside valid grid?
1128 if (! bIsWithinValidGrid(nX, nY))
1129 {
1130 // LogStream << m_ulIter << ": n = " << n << ", m = " << m << ", dLinkingLineLength = " << dLinkingLineLength << ", dCoastDistSoFar = " << dCoastDistSoFar << " (nTotAlongCoastDistanceToDownDriftEndpoint = " << nTotAlongCoastDistanceToDownDriftEndpoint << "), dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " (nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << ") outside valid grid at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1131
1132 // Set for next time
1133 nXLast = nX;
1134 nYLast = nY;
1135 dX += dXInc;
1136 dY += dYInc;
1137
1138 continue;
1139 }
1140
1141 // Not a sea cell?
1142 if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea())
1143 {
1144 // Not a sea cell
1145 // LogStream << m_ulIter << ": n = " << n << ", m = " << m << ", dLinkingLineLength = " << dLinkingLineLength << ", dCoastDistSoFar = " << dCoastDistSoFar << " (nTotAlongCoastDistanceToDownDriftEndpoint = " << nTotAlongCoastDistanceToDownDriftEndpoint << "), dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " (nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << ") not a sea cell at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1146
1147 // Set for next time
1148 nXLast = nX;
1149 nYLast = nY;
1150 dX += dXInc;
1151 dY += dYInc;
1152
1153 continue;
1154 }
1155
1156 // Have we gone past the point where the shadow boundary meets the coast (i.e. the shadow boundary end point)?
1157 if (! bPastShadowEnd)
1158 {
1159 // We have not, so the linking line has two parts: one between the coast and the shadow boundary, one between the shadow boundary and the downdrift boundary
1160 bool bInShadowZone = true;
1161
1162 if (! m_pRasterGrid->m_Cell[nX][nY].bIsinAnyShadowZone())
1163 {
1164 // We have left the shadow zone
1165 // LogStream << "[" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} LEFT SHADOW ZONE" << endl;
1166
1167 bInShadowZone = false;
1168
1169 // Go back over stored cell coords and set their wave properties
1170 for (unsigned int mm = 0; mm < VnShadowCellX.size(); mm++)
1171 {
1172 // Process this shadow zone cell
1173 ProcessShadowZoneCell(VnShadowCellX[mm], VnShadowCellY[mm], nShadowZoneCoastToCapeSeaHand, pPtiCoast, VnShadowCellX.back(), VnShadowCellY.back(), nZone);
1174
1175 // Also process adjacent cells
1176 if (mm > 0)
1177 {
1178 CGeom2DIPoint PtiLeft = PtiGetPerpendicular(VnShadowCellX[mm], VnShadowCellY[mm], VnShadowCellX[mm - 1], VnShadowCellY[mm - 1], 1, RIGHT_HANDED);
1179 CGeom2DIPoint PtiRight = PtiGetPerpendicular(VnShadowCellX[mm], VnShadowCellY[mm], VnShadowCellX[mm - 1], VnShadowCellY[mm - 1], 1, LEFT_HANDED);
1180
1181 if (bIsWithinValidGrid(&PtiLeft))
1182 ProcessShadowZoneCell(PtiLeft.nGetX(), PtiLeft.nGetY(), nShadowZoneCoastToCapeSeaHand, pPtiCoast, VnShadowCellX.back(), VnShadowCellY.back(), nZone);
1183
1184 if (bIsWithinValidGrid(&PtiRight))
1185 ProcessShadowZoneCell(PtiRight.nGetX(), PtiRight.nGetY(), nShadowZoneCoastToCapeSeaHand, pPtiCoast, VnShadowCellX.back(), VnShadowCellY.back(), nZone);
1186 }
1187 }
1188 }
1189
1190 if (bInShadowZone)
1191 {
1192 // Save coords for later
1193 VnShadowCellX.push_back(nX);
1194 VnShadowCellY.push_back(nY);
1195
1196 nShadowZoneLength++;
1197 }
1198
1199 else
1200 {
1201 // In downdrift zone
1202
1203 // LogStream << m_ulIter << ": n = " << n << ", m = " << m << ", dLinkingLineLength = " << dLinkingLineLength << ", dCoastDistSoFar = " << dCoastDistSoFar << " (nTotAlongCoastDistanceToDownDriftEndpoint = " << nTotAlongCoastDistanceToDownDriftEndpoint << "), dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " (nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << ") has [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} in downdrift zone" << endl;
1204
1205 // Process this downdrift cell
1206 ProcessDownDriftCell(nX, nY, (m - nShadowZoneLength), (dLinkingLineLength - nShadowZoneLength), nZone);
1207
1208 // Also process adjacent cells
1209 CGeom2DIPoint PtiLeft = PtiGetPerpendicular(nX, nY, nXLast, nYLast, 1, RIGHT_HANDED);
1210 CGeom2DIPoint PtiRight = PtiGetPerpendicular(nX, nY, nXLast, nYLast, 1, LEFT_HANDED);
1211
1212 if (bIsWithinValidGrid(&PtiLeft))
1213 ProcessDownDriftCell(PtiLeft.nGetX(), PtiLeft.nGetY(), (m - nShadowZoneLength), (dLinkingLineLength - nShadowZoneLength), nZone);
1214
1215 if (bIsWithinValidGrid(&PtiRight))
1216 ProcessDownDriftCell(PtiRight.nGetX(), PtiRight.nGetY(), (m - nShadowZoneLength), (dLinkingLineLength - nShadowZoneLength), nZone);
1217 }
1218 }
1219
1220 else
1221 {
1222 // We have, so the linking line has only one part: between the coast and the downdrift boundary.
1223
1224 // LogStream << m_ulIter << ": n = " << n << ", m = " << m << ", dLinkingLineLength = " << dLinkingLineLength << ", dCoastDistSoFar = " << dCoastDistSoFar << " (nTotAlongCoastDistanceToDownDriftEndpoint = " << nTotAlongCoastDistanceToDownDriftEndpoint << "), dDownDriftBoundaryDistSoFar = " << dDownDriftBoundaryDistSoFar << " (nTotDownDriftBoundaryDistance = " << nTotDownDriftBoundaryDistance << ") has [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} in downdrift zone" << endl;
1225
1226 // Process this downdrift cell
1227 ProcessDownDriftCell(nX, nY, m, dLinkingLineLength, nZone);
1228
1229 // Also process adjacent cells
1230 if ((nXLast != -1) && (nYLast != -1))
1231 {
1232 CGeom2DIPoint PtiLeft = PtiGetPerpendicular(nX, nY, nXLast, nYLast, 1, RIGHT_HANDED);
1233 CGeom2DIPoint PtiRight = PtiGetPerpendicular(nX, nY, nXLast, nYLast, 1, LEFT_HANDED);
1234
1235 if (bIsWithinValidGrid(&PtiLeft))
1236 ProcessDownDriftCell(PtiLeft.nGetX(), PtiLeft.nGetY(), m, dLinkingLineLength, nZone);
1237
1238 if (bIsWithinValidGrid(&PtiRight))
1239 ProcessDownDriftCell(PtiRight.nGetX(), PtiRight.nGetY(), m, dLinkingLineLength, nZone);
1240 }
1241 }
1242
1243 // Set for next time
1244 nXLast = nX;
1245 nYLast = nY;
1246 dX += dXInc;
1247 dY += dYInc;
1248 }
1249 }
1250}
1251
1252//===============================================================================================================================
1254//===============================================================================================================================
1255void CSimulation::ProcessDownDriftCell(int const nX, int const nY, int const nTraversed, double const dTotalToTraverse, int const nZone)
1256{
1257 // Get the pre-existing (i.e. shore-parallel) wave height
1258 double dWaveHeight = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight();
1259
1260 if (bFPIsEqual(dWaveHeight, DBL_NODATA, TOLERANCE))
1261 {
1262 // Is not a sea cell
1263 // LogStream << m_ulIter << ": [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} ignored, not a sea cell" << endl;
1264
1265 return;
1266 }
1267
1268 int nZoneCode = m_pRasterGrid->m_Cell[nX][nY].nGetShadowZoneNumber();
1269
1270 if (nZoneCode == (nZone + 1))
1271 {
1272 // This cell is in the associated shadow zone, so don't change it
1273 // LogStream << m_ulIter << ": [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} ignored, is in associated shadow zone (" << nZone+1 << ")" << endl;
1274
1275 return;
1276 }
1277
1278 if (nZoneCode < 0)
1279 {
1280 // This cell is in a shadow zone but is not yet processed, so don't change it
1281 // LogStream << m_ulIter << ": [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} ignored, is an unprocessed cell in shadow zone " << nZoneCode << endl;
1282
1283 return;
1284 }
1285
1286 nZoneCode = m_pRasterGrid->m_Cell[nX][nY].nGetDownDriftZoneNumber();
1287
1288 if (nZoneCode == (nZone + 1))
1289 {
1290 // We have already processed this cell for this downdrift zone
1291 // LogStream << m_ulIter << ": [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "} ignored, already done for this down-drift zone " << nZone+1 << endl;
1292
1293 return;
1294 }
1295
1296 // OK, we are downdrift of the shadow zone area and have not yet processed this cell for this zone, so mark it
1297 m_pRasterGrid->m_Cell[nX][nY].SetDownDriftZoneNumber(nZone + 1);
1298
1299 // Equation 14 from Hurst et al. TODO 056 Check this! Could not get this to work (typo in paper?), so used the equation below instead
1300 // double dKp = 0.5 * (1.0 - sin((PI * 90.0 * nSweep) / (180.0 * nSweepLength)));
1301 double dKp = 0.5 + (0.5 * sin((PI * nTraversed) / (2.0 * dTotalToTraverse)));
1302
1303 // Set the modified wave height
1304 m_pRasterGrid->m_Cell[nX][nY].SetWaveHeight(dKp * dWaveHeight);
1305
1306 // LogStream << m_ulIter << ": [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, nTraversed = " << nTraversed << " dTotalToTraverse = " << dTotalToTraverse << " fraction traversed = " << nTraversed / dTotalToTraverse << endl << "m_pRasterGrid->m_Cell[" << nX << "][" << nY << "].dGetCellDeepWaterWaveHeight() = " << m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight() << " m, original dWaveHeight = " << dWaveHeight << " m, dKp = " << dKp << ", modified wave height = " << dKp * dWaveHeight << " m" << endl << endl;
1307}
1308
1309//===============================================================================================================================
1311//===============================================================================================================================
1312void CSimulation::ProcessShadowZoneCell(int const nX, int const nY, int const nShadowZoneCoastToCapeSeaHand, CGeom2DIPoint const* pPtiCoast, int const nShadowEndX, int const nShadowEndY, int const nZone)
1313{
1314 int nZoneCode = m_pRasterGrid->m_Cell[nX][nY].nGetShadowZoneNumber();
1315
1316 if (nZoneCode == (-nZone - 1))
1317 {
1318 // OK, we are in the shadow zone and have not already processed this cell, so mark it (a +ve number, starting from 1)
1319 m_pRasterGrid->m_Cell[nX][nY].SetShadowZoneNumber(nZone + 1);
1320
1321 // Next calculate wave angle here: first calculate dOmega, the signed angle subtended between this end point and the start point, and this end point and the end of the shadow boundary
1322 CGeom2DIPoint PtiThis(nX, nY);
1323 CGeom2DIPoint PtiShadowBoundary(nShadowEndX, nShadowEndY);
1324 double dOmega = 180 * dAngleSubtended(pPtiCoast, &PtiThis, &PtiShadowBoundary) / PI;
1325
1326 // If dOmega is 90 degrees or more in either direction, set both wave angle and wave height to zero
1327 if (tAbs(dOmega) >= 90)
1328 {
1329 m_pRasterGrid->m_Cell[nX][nY].SetWaveAngle(0);
1330 m_pRasterGrid->m_Cell[nX][nY].SetWaveHeight(0);
1331
1332 // LogStream << m_ulIter << ": on shadow linking line with coast end [" << pPtiCoast->nGetX() << "][" << pPtiCoast->nGetY() << "] = {" << dGridCentroidXToExtCRSX(pPtiCoast->nGetX()) << ", " << dGridCentroidYToExtCRSY(pPtiCoast->nGetY()) << "} and shadow boundary end [" << PtiShadowBoundary.nGetX() << "][" << PtiShadowBoundary.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiShadowBoundary.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiShadowBoundary.nGetY()) << "}, this point [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl << "angle subtended = " << dOmega << " degrees, m_pRasterGrid->m_Cell[" << nX << "][" << nY << "].dGetCellDeepWaterWaveHeight() = " << m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight() << " degrees, wave orientation = 0 degrees, wave height = 0 m" << endl;
1333 }
1334
1335 else
1336 {
1337 // Adapted from equation 12 in Hurst et al.
1338 double dDeltaShadowWaveAngle = 1.5 * dOmega;
1339
1340 // Get the pre-existing (i.e. shore-parallel) wave orientation
1341 double dWaveAngle = m_pRasterGrid->m_Cell[nX][nY].dGetWaveAngle();
1342
1343 double dShadowWaveAngle;
1344
1345 if (nShadowZoneCoastToCapeSeaHand == LEFT_HANDED)
1346 dShadowWaveAngle = dWaveAngle + dDeltaShadowWaveAngle;
1347
1348 else
1349 dShadowWaveAngle = dWaveAngle - dDeltaShadowWaveAngle;
1350
1351 // Set the shadow zone wave orientation
1352 m_pRasterGrid->m_Cell[nX][nY].SetWaveAngle(dKeepWithin360(dShadowWaveAngle));
1353
1354 // Now calculate wave height within the shadow zone, use equation 13 from Hurst et al.
1355 double dKp = 0.5 * cos(dOmega * PI / 180);
1356
1357 // Get the pre-existing (i.e. shore-parallel) wave height
1358 double dWaveHeight = m_pRasterGrid->m_Cell[nX][nY].dGetWaveHeight();
1359
1360 // Set the shadow zone wave height
1361 m_pRasterGrid->m_Cell[nX][nY].SetWaveHeight(dKp * dWaveHeight);
1362
1363 // LogStream << m_ulIter << ": on shadow linking line with coast end [" << pPtiCoast->nGetX() << "][" << pPtiCoast->nGetY() << "] = {" << dGridCentroidXToExtCRSX(pPtiCoast->nGetX()) << ", " << dGridCentroidYToExtCRSY(pPtiCoast->nGetY()) << "} and shadow boundary end [" << PtiShadowBoundary.nGetX() << "][" << PtiShadowBoundary.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiShadowBoundary.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiShadowBoundary.nGetY()) << "}, this point [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, angle subtended = " << dOmega << " degrees, m_pRasterGrid->m_Cell[" << nX << "][" << nY << "].dGetCellDeepWaterWaveHeight() = " << m_pRasterGrid->m_Cell[nX][nY].dGetCellDeepWaterWaveHeight() << " m, dDeltaShadowWaveAngle = " << dDeltaShadowWaveAngle << " degrees, dWaveAngle = " << dWaveAngle << " degrees, dShadowWaveAngle = " << dShadowWaveAngle << " degrees, dWaveHeight = " << dWaveHeight << " m, dKp = " << dKp << ", shadow zone wave height = " << dKp * dWaveHeight << " m" << endl;
1364 }
1365 }
1366}
CGeom2DIPoint & Back(void)
Returns the last integer point from the vector which represents this 2D shape.
Definition 2di_shape.cpp:46
void AppendIfNotAlready(int const, int const)
Appends a new integer point to the vector which represents this 2D shape, but only if the point is no...
Definition 2di_shape.cpp:93
void Append(CGeom2DIPoint const *)
Appends a new integer point to the vector which represents this 2D shape.
Definition 2di_shape.cpp:81
vector< CGeom2DIPoint > * pPtiVGetPoints(void)
Returns the address of the vector which represents this 2D shape.
Definition 2di_shape.cpp:52
void Append(CGeom2DPoint const *)
Appends a point to this 2D shape.
Definition 2d_shape.cpp:67
void Reverse(void)
Reverses the sequence of points in the vector which represents this 2D polygon.
Definition 2d_shape.cpp:166
CGeom2DPoint * pPtBack(void)
Returns the last element of this 2D shape.
Definition 2d_shape.cpp:91
void AppendIfNotAlready(double const, double const)
Appends a point to this 2D shape only if it isn't already in the shape vector.
Definition 2d_shape.cpp:79
int nGetSize(void) const
Definition 2d_shape.cpp:56
Geometry class used to represent 2D point objects with integer coordinates.
Definition 2di_point.h:29
void SetY(int const)
The integer parameter sets a value for the CGeom2DIPoint object's Y coordinate.
Definition 2di_point.cpp:72
int nGetY(void) const
Returns the CGeom2DIPoint object's integer Y coordinate.
Definition 2di_point.cpp:48
void SetX(int const)
The integer parameter sets a value for the CGeom2DIPoint object's X coordinate.
Definition 2di_point.cpp:66
int nGetX(void) const
Returns the CGeom2DIPoint object's integer X coordinate.
Definition 2di_point.cpp:42
Geometry class used to represent 2D point objects with floating-point coordinates.
Definition 2d_point.h:27
Geometry class used to represent 2D vector integer line objects.
Definition i_line.h:31
bool bIsPresent(int const, int const)
Returns true if the point is present in the line.
Definition i_line.cpp:72
Geometry class used to represent 2D vector line objects.
Definition line.h:31
int m_nLogFileDetail
Definition simulation.h:591
CGeomRasterGrid * m_pRasterGrid
Pointer to the raster grid object.
int m_nXGridSize
The size of the grid in the x direction.
Definition simulation.h:462
int nFloodFillShadowZone(int const, CGeom2DIPoint const *, CGeom2DIPoint const *, CGeom2DIPoint const *)
Cell-by-cell fills a shadow zone from the centroid.
ofstream LogStream
vector< CRWCoast > m_VCoast
The coastline objects.
int m_nYGridSize
The size of the grid in the y direction.
Definition simulation.h:465
static CGeom2DIPoint PtiGetPerpendicular(CGeom2DIPoint const *, CGeom2DIPoint const *, double const, int const)
Returns a CGeom2DIPoint (grid CRS) which is the 'other' point of a two-point vector passing through P...
void ProcessShadowZoneCell(int const, int const, int const, CGeom2DIPoint const *, int const, int const, int const)
Process a single cell which is in the shadow zone, changing its wave height and orientation.
static double dGetDistanceBetween(CGeom2DPoint const *, CGeom2DPoint const *)
Returns the distance (in external CRS) between two points.
int nDoAllShadowZones(void)
Finds wave shadow zones and modifies waves in and near them. Note that where up-coast and down-coast ...
static double dKeepWithin360(double const)
Constrains the supplied angle to be within 0 and 360 degrees.
double dGridCentroidYToExtCRSY(int const) const
Given the integer Y-axis ordinate of a cell in the raster grid CRS, returns the external CRS Y-axis o...
Definition gis_utils.cpp:70
static bool bOnOrOffShoreAndUpOrDownCoast(double const, double const, int const, bool &)
Determines whether the wave orientation at this point on a coast is onshore or offshore,...
static CGeom2DIPoint PtiFollowWaveAngle(CGeom2DIPoint const *, double const, double &)
Given a cell and a wave orientation, finds the 'upwave' cell.
static double dAngleSubtended(CGeom2DIPoint const *, CGeom2DIPoint const *, CGeom2DIPoint const *)
Returns the signed angle BAC (in radians) subtended between three CGeom2DIPoints B A C....
double dExtCRSXToGridX(double const) const
Transforms an X-axis ordinate in the external CRS to the equivalent X-axis ordinate in the raster gri...
static CGeom2DIPoint PtiWeightedAverage(CGeom2DIPoint const *, CGeom2DIPoint const *, double const)
Returns an integer point (grid CRS) which is the weighted average of two other grid CRS integer point...
double dExtCRSYToGridY(double const) const
Transforms a Y-axis ordinate in the external CRS to the equivalent Y-axis ordinate in the raster grid...
bool bIsWithinValidGrid(int const, int const) const
Checks whether the supplied point (an x-y pair, in the grid CRS) is within the raster grid,...
void DoShadowZoneAndDownDriftZone(int const, int const, int const, int const)
Traverse the shadow zone, changing wave orientation and height, and the down-drift zone,...
unsigned long m_ulIter
The number of the current iteration (time step)
Definition simulation.h:618
double dGridCentroidXToExtCRSX(int const) const
Given the integer X-axis ordinate of a cell in the raster grid CRS, returns the external CRS X-axis o...
Definition gis_utils.cpp:62
void ProcessDownDriftCell(int const, int const, int const, double const, int const)
Process a single cell which is in the downdrift zone, changing its wave height.
double m_dCellSide
Length of a cell side (in external CRS units)
Definition simulation.h:689
static CGeom2DIPoint PtiPolygonCentroid(vector< CGeom2DIPoint > *)
Returns an integer point (grid CRS) which is the centroid of a polygon, given by a vector of grid CRS...
This file contains global definitions for CoastalME.
int const INT_NODATA
Definition cme.h:474
int const MAX_LEN_SHADOW_LINE_TO_IGNORE
Definition cme.h:476
T tMin(T a, T b)
Definition cme.h:1251
double const TOLERANCE
Definition cme.h:811
string const ERR
Definition cme.h:890
bool const CREATE_SHADOW_ZONE_IF_HITS_GRID_EDGE
Definition cme.h:456
int const RTN_ERR_SHADOW_ZONE_FLOOD_START_POINT
Definition cme.h:744
int const MAX_NUM_PREV_ORIENTATION_VALUES
Definition cme.h:477
int const RTN_ERR_SHADOW_ZONE_FLOOD_FILL_NOGRID
Definition cme.h:743
int const LOG_FILE_MIDDLE_DETAIL
Definition cme.h:489
double const PI
Definition cme.h:793
bool bFPIsEqual(const T d1, const T d2, const T dEpsilon)
Definition cme.h:1284
T tMax(T a, T b)
Definition cme.h:1240
int const LOG_FILE_HIGH_DETAIL
Definition cme.h:490
double const MIN_LENGTH_OF_SHADOW_ZONE_LINE
Definition cme.h:817
string const WARN
Definition cme.h:891
int const SOUTH
Definition cme.h:499
int const LOG_FILE_ALL
Definition cme.h:491
int const EAST
Definition cme.h:497
int const RTN_OK
Definition cme.h:692
double const DBL_NODATA
Definition cme.h:822
double const MAX_LAND_LENGTH_OF_SHADOW_ZONE_LINE
Definition cme.h:818
int const RIGHT_HANDED
Definition cme.h:509
T tAbs(T a)
Definition cme.h:1261
int const NORTH
Definition cme.h:495
int const LEFT_HANDED
Definition cme.h:510
int const WEST
Definition cme.h:501
Contains CRWCoast definitions.
Contains CGeomRasterGrid definitions.
Contains CSimulation definitions.
int nRound(double const d)
Version of the above that returns an int.