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