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