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