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