CoastalME (Coastal Modelling Environment)
Simulates the long-term behaviour of complex coastlines
Loading...
Searching...
No Matches
create_profiles.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
26#include <cmath>
27#include <cfloat>
28
29#include <iostream>
30using std::cerr;
31using std::cout;
32using std::endl;
33using std::ios;
34
35#include <iomanip>
36using std::setiosflags;
37using std::setprecision;
38
39#include <algorithm>
40using std::find;
41using std::sort;
42
43#include <utility>
44using std::make_pair;
45using std::pair;
46
47#include <random>
48using std::normal_distribution;
49
50#include "cme.h"
51#include "simulation.h"
52#include "coast.h"
53
54//===============================================================================================================================
56//===============================================================================================================================
57bool bCurvaturePairCompareDescending(const pair<int, double>& prLeft, const pair<int, double>& prRight)
58{
59 // Sort in descending order (i.e. most concave first)
60 return prLeft.second > prRight.second;
61}
62
63//===============================================================================================================================
65//===============================================================================================================================
67{
69 LogStream << m_ulIter << ": Creating profiles" << endl;
70
71 int nProfileGlobalID = -1;
72
73 for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++)
74 {
75 int nProfile = 0;
76 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
77
78 // Create a bool vector to mark coast points which have been searched
79 vector<bool> bVCoastPointDone(nCoastSize, false);
80
81 // Now create a vector of pairs: the first value of the pair is the coastline point, the second is the coastline's curvature at that point
82 vector<pair<int, double>> prVCurvature;
83 for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++)
84 {
85 double dCurvature;
86 if (m_VCoast[nCoast].pGetCoastLandform(nCoastPoint)->nGetLandFormCategory() != LF_CAT_INTERVENTION)
87 {
88 // Not an intervention coast point, so store the smoothed curvature
89 dCurvature = m_VCoast[nCoast].dGetSmoothCurvature(nCoastPoint);
90 }
91 else
92 {
93 // This is an intervention coast point, which is likely to have some sharp angles. So store the detailed curvature
94 dCurvature = m_VCoast[nCoast].dGetDetailedCurvature(nCoastPoint);
95 }
96 prVCurvature.push_back(make_pair(nCoastPoint, dCurvature));
97 }
98
99 // Sort this pair vector in descending order, so that the most convex curvature points are first
100 sort(prVCurvature.begin(), prVCurvature.end(), bCurvaturePairCompareDescending);
101
102 // // DEBUG CODE =======================================================================================================================
103 // for (int n = 0; n < prVCurvature.size(); n++)
104 // {
105 // LogStream << prVCurvature[n].first << "\t" << prVCurvature[n].second << endl;
106 // }
107 // LogStream << endl << endl;
108 // // DEBUG CODE =======================================================================================================================
109
110 // And mark points at and near the start and end of the coastline so that they don't get searched (will be creating 'special' start- and end-of-coast profiles at these end points later)
111 for (int n = 0; n < m_nCoastNormalAvgSpacing; n++)
112 {
113 if (n < nCoastSize)
114 bVCoastPointDone[n] = true;
115
116 int m = nCoastSize - n - 1;
117 if (m >= 0)
118 bVCoastPointDone[m] = true;
119 }
120
121 // Now locate the start points for all coastline-normal profiles (except the grid-edge ones), at points of maximum convexity. Then create the profiles
122 LocateAndCreateProfiles(nCoast, nProfile, nProfileGlobalID, m_nCoastNormalAvgSpacing, &bVCoastPointDone, &prVCurvature);
123
124 // Did we fail to create any normal profiles? If so, quit
125 if (nProfile < 0)
126 {
127 string strErr = ERR + "timestep " + strDblToStr(m_ulIter) + ": could not create profiles for coastline " + strDblToStr(nCoast);
128 if (m_ulIter == 1)
129 strErr += ". Check the SWL";
130 strErr += "\n";
131
132 cerr << strErr;
133 LogStream << strErr;
134
136 }
137
138 // Locate and create a 'special' profile at the grid edge, first at the beginning of the coastline. Then put this onto the raster grid
139 int nRet = nLocateAndCreateGridEdgeProfile(true, nCoast, nProfile, nProfileGlobalID);
140 if (nRet != RTN_OK)
141 return nRet;
142
143 // Locate a second 'special' profile at the grid edge, this time at end of the coastline. Then put this onto the raster grid
144 nRet = nLocateAndCreateGridEdgeProfile(false, nCoast, ++nProfile, nProfileGlobalID);
145 if (nRet != RTN_OK)
146 return nRet;
147
148 // Insert pointers to profiles at coastline points in the profile-all-coastpoint index
149 m_VCoast[nCoast].InsertProfilesInProfileCoastPointIndex();
150
151 // // DEBUG CODE ===================================================================================================
152 // LogStream << endl << "===========================================================================================" << endl;
153 // LogStream << "PROFILES BEFORE ADDING BEFORE- AND AFTER-PROFILE NUMBERS" << endl;
154 // int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles();
155 // for (int nn = 0; nn < nNumProfiles; nn++)
156 // {
157 // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nn);
158 //
159 // LogStream << nn << " nCoastID = " << pProfile->nGetCoastID() << " nGlobalID = " << pProfile->nGetCoastID() << " nGetCoastPoint = " << pProfile->nGetCoastPoint() << " pGetUpCoastAdjacentProfile = " << pProfile->pGetUpCoastAdjacentProfile() << " pGetDownCoastAdjacentProfile = " << pProfile->pGetDownCoastAdjacentProfile() << endl;
160 // }
161 // LogStream << "===================================================================================================" << endl << endl;
162 // // DEBUG CODE ===================================================================================================
163
164 // // DEBUG CODE ===================================================================================================
165 // int nProf = 0;
166 // for (int n = 0; n < nCoastSize; n++)
167 // {
168 // if (m_VCoast[nCoast].bIsProfileAtCoastPoint(n))
169 // {
170 // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(n);
171 //
172 // LogStream << "profile " << pProfile->nGetCoastID() << " at coast point " << n << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl;
173 //
174 // nProf++;
175 // }
176 // }
177 // LogStream << endl;
178 // LogStream << "nProf = " << nProf << endl;
179 // // DEBUG CODE ===================================================================================================
180
181 // int nLastProfile;
182 // int nThisProfile;
183 CGeomProfile* pLastProfile;
184 CGeomProfile* pThisProfile;
185
186 // Go along the coastline and give each profile the number of the adjacent up-coast profile and the adjacent down-coast profile
187 for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++)
188 {
189 if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nCoastPoint))
190 {
191 pThisProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nCoastPoint);
192 // nThisProfile = pThisProfile->nGetCoastID();
193
194 if (nCoastPoint == 0)
195 {
196 pThisProfile->SetUpCoastAdjacentProfile(NULL);
197
198 // LogStream << "nCoastPoint = " << nCoastPoint << " ThisProfile = " << nThisProfile << " ThisProfile UpCoast = " << pThisProfile->pGetUpCoastAdjacentProfile() << " ThisProfile DownCoast = " << pThisProfile->pGetDownCoastAdjacentProfile() << endl;
199
200 pLastProfile = pThisProfile;
201 // nLastProfile = nThisProfile;
202 continue;
203 }
204
205 // nLastProfile = pLastProfile->nGetCoastID();
206 pLastProfile->SetDownCoastAdjacentProfile(pThisProfile);
207 pThisProfile->SetUpCoastAdjacentProfile(pLastProfile);
208
209 if (nCoastPoint == nCoastSize-1)
210 pThisProfile->SetDownCoastAdjacentProfile(NULL);
211
212 // LogStream << "nCoastPoint = " << nCoastPoint << " LastProfile = " << nLastProfile << " LastProfile UpCoast = " << pLastProfile->pGetUpCoastAdjacentProfile() << " LastProfile DownCoast = " << pLastProfile->pGetDownCoastAdjacentProfile() << " ThisProfile = " << nThisProfile << " ThisProfile UpCoast = " << pThisProfile->pGetUpCoastAdjacentProfile() << " ThisProfile DownCoast = " << pThisProfile->pGetDownCoastAdjacentProfile() << endl;
213
214 pLastProfile = pThisProfile;
215 // nLastProfile = nThisProfile;
216 }
217 }
218
219 // And create an index to this coast's profiles in along-coastline sequence
220 m_VCoast[nCoast].CreateProfileDownCoastIndex();
221
222 // // DEBUG CODE =======================================================================================================================
223 // for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++)
224 // {
225 // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n);
226 // CGeomProfile* pUpCoastProfile = pProfile->pGetUpCoastAdjacentProfile();
227 // CGeomProfile* pDownCoastProfile = pProfile->pGetDownCoastAdjacentProfile();
228 // int nUpCoastProfile = INT_NODATA;
229 // int nDownCoastProfile = INT_NODATA;
230 // if (pUpCoastProfile != 0)
231 // nUpCoastProfile = pUpCoastProfile->nGetCoastID();
232 // if (pDownCoastProfile != 0)
233 // nDownCoastProfile = pDownCoastProfile->nGetCoastID();
234 // LogStream << n << "\t nCoastID = " << pProfile->nGetCoastID() << "\tnGlobalID = " << pProfile->nGetGlobalID() << "\t up-coast profile = " << nUpCoastProfile << "\t down-coast profile = " << nDownCoastProfile << endl;
235 // }
236 // LogStream << endl;
237 // // DEBUG CODE =======================================================================================================================
238
239// // DEBUG CODE =======================================================================================================================
240// int nProf = 0;
241// for (int n = 0; n < nCoastSize; n++)
242// {
243// // LogStream << n << "\t";
244//
245// // LogStream << m_VCoast[nCoast].dGetDetailedCurvature(n) << "\t";
246// //
247// // LogStream << m_VCoast[nCoast].dGetSmoothCurvature(n) << "\t";
248// //
249// // if (m_VCoast[nCoast].pGetCoastLandform(n)->nGetLandFormCategory() == LF_CAT_INTERVENTION)
250// // LogStream << "I\t";
251//
252// if (m_VCoast[nCoast].bIsProfileAtCoastPoint(n))
253// {
254// CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(n);
255//
256// LogStream << "profile " << pProfile->nGetCoastID() << " at coast point " << n << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl;
257//
258// nProf++;
259// }
260// }
261// LogStream << endl;
262// LogStream << "nProf = " << nProf << endl;
263// // DEBUG CODE =======================================================================================================================
264
265 }
266
267 return RTN_OK;
268}
269
270//===============================================================================================================================
272//===============================================================================================================================
273void CSimulation::LocateAndCreateProfiles(int const nCoast, int& nProfile, int& nProfileGlobalID, int const nProfileHalfAvgSpacing, vector<bool>* pbVCoastPointDone, vector<pair<int, double>> const* prVCurvature)
274{
275 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
276
277 // Work along the vector of curvature pairs starting at the convex end
278 for (int n = nCoastSize - 1; n >= 0; n--)
279 {
280 // Have we searched all the coastline points?
281 int nStillToSearch = 0;
282 for (int m = 0; m < nCoastSize; m++)
283 if (! pbVCoastPointDone->at(m))
284 nStillToSearch++;
285
286 if (nStillToSearch == 0)
287 // OK we are done here
288 return;
289
290 // This convex point on the coastline is a potential location for a normal
291 int nNormalPoint = prVCurvature->at(n).first;
292
293 // Ignore each end of the coastline
294 if ((nNormalPoint == 0) || (nNormalPoint == nCoastSize - 1))
295 continue;
296
297 // TODO 089 When choosing locations for profiles, do coast first then interventions
298
299 if (! pbVCoastPointDone->at(nNormalPoint))
300 {
301 // We have not already searched this coast point. Is it an intervention coast point?
302 bool bIntervention = false;
303 if (m_VCoast[nCoast].pGetCoastLandform(nNormalPoint)->nGetLandFormCategory() == LF_CAT_INTERVENTION)
304 {
305 // It is an intervention
306 bIntervention = true;
307 }
308
309 CGeom2DIPoint PtiThis = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nNormalPoint);
310
311 // Create a profile here
312 int nRet = nCreateProfile(nCoast, nCoastSize, nNormalPoint, nProfile, nProfileGlobalID, bIntervention, &PtiThis);
313
314 // // DEBUG CODE =================
315 // LogStream << "After nCreateProfile() ===========" << endl;
316 // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile);
317 // LogStream << pProfile->nGetCoastID() << "\t" << pProfile->nGetGlobalID() << "\t";
318 //
319 // int nPointsInProfile = pProfile->nGetProfileSize();
320 //
321 // for (int nPoint = 0; nPoint < nPointsInProfile; nPoint++)
322 // {
323 // CGeom2DPoint Pt = *pProfile->pPtGetPointInProfile(nPoint);
324 // LogStream << " {" << Pt.dGetX() << ", " << Pt.dGetY() << "}";
325 // }
326 // LogStream << endl << "===========" << endl;
327 // // DEBUG CODE =================
328
329 // Mark this coast point as searched
330 pbVCoastPointDone->at(nNormalPoint) = true;
331
332 if (nRet != RTN_OK)
333 {
334 // This potential profile is no good (has hit coast, or hit dry land, etc.) so forget about it
335 // LogStream << "Profile is no good" << endl;
336 continue;
337 }
338
339 // // DEBUG CODE ===================================================================================================
340 // LogStream << endl << "===========================================================================================" << endl;
341 // LogStream << "PROFILES JUST AFTER CREATION" << endl;
342 // int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles();
343 // for (int nn = 0; nn < nNumProfiles; nn++)
344 // {
345 // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nn);
346 //
347 // LogStream << nn << " nCoastID = " << pProfile->nGetCoastID() << " nGlobalID = " << pProfile->nGetCoastID() << " nGetCoastPoint = " << pProfile->nGetCoastPoint() << " pGetUpCoastAdjacentProfile = " << pProfile->pGetUpCoastAdjacentProfile() << " pGetDownCoastAdjacentProfile = " << pProfile->pGetDownCoastAdjacentProfile() << endl;
348 // }
349 // LogStream << "===================================================================================================" << endl << endl;
350 // // DEBUG CODE ===================================================================================================
351 //
352 // // DEBUG CODE ===================================================================================================
353 // LogStream << "++++++++++++++++++++++" << endl;
354 // LogStream << endl << "Just created profile " << nProfile << endl;
355 // int nProf = 0;
356 // for (int nnn = 0; nnn < nCoastSize; nnn++)
357 // {
358 // if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nnn))
359 // {
360 // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nnn);
361 //
362 // LogStream << "profile " << pProfile->nGetCoastID() << " at coast point " << nnn << " adjacent up-coast profile = " << pProfile->pGetUpCoastAdjacentProfile() << " adjacent down-coast profile = " << pProfile->pGetDownCoastAdjacentProfile() << endl;
363 //
364 // nProf++;
365 // }
366 // }
367 // LogStream << endl;
368 // LogStream << "nProf = " << nProf << endl;
369 // LogStream << "++++++++++++++++++++++" << endl;
370 // // DEBUG CODE ===================================================================================================
371
372 // CGeom2DPoint PtThis = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nNormalPoint);
373 // if (m_nLogFileDetail >= LOG_FILE_ALL)
374 // LogStream << m_ulIter << ": Coast " << nCoast << " profile " << nProfile << " created at coast point " << nNormalPoint << " [" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] = {" << PtThis.dGetX() << ", " << PtThis.dGetY() << "} (smoothed curvature = " << m_VCoast[nCoast].dGetSmoothCurvature(nNormalPoint) << ", detailed curvature = " << m_VCoast[nCoast].dGetDetailedCurvature(nNormalPoint) << ")" << endl;
375
376 // // DEBUG CODE =================================================================================
377 // if (m_pRasterGrid->m_Cell[PtiThis.nGetX()][PtiThis.nGetY()].bIsCoastline())
378 // LogStream << m_ulIter << ": cell[" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] IS coastline" << endl;
379 // else
380 // LogStream << m_ulIter << ": ******* cell[" << PtiThis.nGetX() << "][" << PtiThis.nGetY() << "] IS NOT coastline" << endl;
381 // // DEBUG CODE =================================================================================
382
383 // This profile is fine
384 nProfile++;
385
386 // We need to mark points on either side of this profile so that we don't get profiles which are too close together. This is straightforward for non-intervention profiles, but best-placed profiles on narrow intervention structures may need to be quite close. So guess in a value for number of points to mark on interventions
387 double dNumToMark = nProfileHalfAvgSpacing;
388 if (bIntervention)
389 dNumToMark = nProfileHalfAvgSpacing / 4; // TODO 011
390
391 // If we have a random factor for profile spacing, then modify the profile spacing
393 {
394 // Draw a sample from the unit normal distribution using random number generator 0
395 double dRand = m_dUnitNormalDist(m_Rand[0]);
396
397 double dTmp = dRand * m_dCoastNormalRandSpacingFactor * dNumToMark;
398 dNumToMark += dTmp;
399
400 // Make sure number to mark is not too small TODO 011
401 dNumToMark = tMax(dNumToMark, nProfileHalfAvgSpacing / 4.0);
402
403 // TODO 014 Assume that the above is the profile spacing on straight bits of coast. Try gradually increasing the profile spacing with increasing concavity, and decreasing the profile spacing with increasing convexity. Could use a Michaelis-Menten S-curve relationship for this i.e.
404// double fReN = pow(NowCell[nX][nY].dGetReynolds(m_dNu), m_dDepN);
405// double fC1 = m_dC1Laminar - ((m_dC1Diff * fReN) / (fReN + m_dReMidN));
406 }
407
408 // Mark points on either side of the profile
409 for (int m = 1; m < dNumToMark; m++)
410 {
411 int nTmpPoint = nNormalPoint + m;
412 if (nTmpPoint < nCoastSize)
413 pbVCoastPointDone->at(nTmpPoint) = true;
414
415 nTmpPoint = nNormalPoint - m;
416 if (nTmpPoint >= 0)
417 pbVCoastPointDone->at(nTmpPoint) = true;
418 }
419 }
420 }
421}
422
423//===============================================================================================================================
425//===============================================================================================================================
426int CSimulation::nCreateProfile(int const nCoast, int const nCoastSize, int const nProfileStartPoint, int const nProfile, int& pnProfileGlobalID, bool const bIntervention, CGeom2DIPoint const* pPtiStart)
427{
428 // OK, we have flagged the start point of this new coastline-normal profile, so create it. Make the start of the profile the centroid of the actual cell that is marked as coast (not the cell under the smoothed vector coast, they may well be different)
429 CGeom2DPoint PtStart; // In external CRS
430 PtStart.SetX(dGridCentroidXToExtCRSX(pPtiStart->nGetX()));
431 PtStart.SetY(dGridCentroidYToExtCRSY(pPtiStart->nGetY()));
432
433 CGeom2DPoint PtEnd; // In external CRS
434 CGeom2DIPoint PtiEnd; // In grid CRS
435 int nRet = nGetCoastNormalEndPoint(nCoast, nProfileStartPoint, nCoastSize, &PtStart, m_dCoastNormalLength, &PtEnd, &PtiEnd, bIntervention);
437 {
438 // Could not solve end-point equation, so forget about this profile
439 return nRet;
440 }
441
442 int nXEnd = PtiEnd.nGetX();
443 int nYEnd = PtiEnd.nGetY();
444
445 // Safety check: is the end point in the contiguous sea?
446 if (! m_pRasterGrid->m_Cell[nXEnd][nYEnd].bIsInContiguousSea())
447 {
448 // if (m_nLogFileDetail >= LOG_FILE_ALL)
449 // LogStream << m_ulIter << ": coast " << nCoast << ", possible profile with start point " << nProfileStartPoint << " has inland end point at [" << nXEnd << "][" << nYEnd << "] = {" << dGridCentroidXToExtCRSX(nXEnd) << ", " << dGridCentroidYToExtCRSY(nYEnd) << "}, ignoring" << endl;
450
452 }
453
454 // Safety check: is the water depth at the end point less than the depth of closure?
455 if (m_pRasterGrid->m_Cell[nXEnd][nYEnd].dGetSeaDepth() < m_dDepthOfClosure)
456 {
457 // if (m_nLogFileDetail >= LOG_FILE_ALL)
458 // LogStream << m_ulIter << ": coast " << nCoast << ", possible profile with start point " << nProfileStartPoint << " is too short for depth of closure " << m_dDepthOfClosure << " at end point [" << nXEnd << "][" << nYEnd << "] = {" << dGridCentroidXToExtCRSX(nXEnd) << ", " << dGridCentroidYToExtCRSY(nYEnd) << "}, ignoring" << endl;
459
461 }
462
463 // No problems, so create the new profile
464 CGeomProfile* pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, ++pnProfileGlobalID, pPtiStart, &PtiEnd, bIntervention);
465
466 // And create the profile's coastline-normal vector. Only two points (start and end points, both external CRS) are stored
467 vector<CGeom2DPoint> VNormal;
468 VNormal.push_back(PtStart);
469 VNormal.push_back(PtEnd);
470
471 // Set the start and end points (external CRS) of the profile
472 pProfile->SetPointsInProfile(&VNormal);
473
474 // Create the profile's CGeomMultiLine then set nProfile as the only co-incident profile of the only line segment
475 pProfile->AppendLineSegment();
476 pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0));
477
478 // Save the profile, note that several fields in the profile are still blank
479 m_VCoast[nCoast].AppendProfile(pProfile);
480
481 // // DEBUG CODE =================
482 // LogStream << "in nCreateProfile() ===========" << endl;
483 // // CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile);
484 // LogStream << pProfile->nGetCoastID() << "\t" << pProfile->nGetGlobalID() << "\t";
485 //
486 // int nPointsInProfile = pProfile->nGetProfileSize();
487 //
488 // for (int nPoint = 0; nPoint < nPointsInProfile; nPoint++)
489 // {
490 // CGeom2DPoint Pt = *pProfile->pPtGetPointInProfile(nPoint);
491 // LogStream << " {" << Pt.dGetX() << ", " << Pt.dGetY() << "}";
492 // }
493 // LogStream << endl << "===========" << endl;
494 // // DEBUG CODE =================
495
496 LogStream << m_ulIter << ": coast " << nCoast << " profile " << nProfile << " (nCoastID = " << pProfile->nGetCoastID() << " nGlobalID = " << pProfile->nGetGlobalID() << ") created at coast point (" << nProfileStartPoint << ") from [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "] = {" << PtStart.dGetX() << ", " << PtStart.dGetY() << "} to [" << PtiEnd.nGetX() << "][" << PtiEnd.nGetY() << "] = {" << PtEnd.dGetX() << ", " << PtEnd.dGetY() << "}" << (pProfile->bIsIntervention() ? ", from intervention" : "") << endl;
497
498 return RTN_OK;
499}
500
501//===============================================================================================================================
503//===============================================================================================================================
504int CSimulation::nLocateAndCreateGridEdgeProfile(bool const bCoastStart, int const nCoast, int &nProfile, int& nProfileGlobalID)
505{
506 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
507 int nHandedness = m_VCoast[nCoast].nGetSeaHandedness();
508 int nProfileLen = nRound(m_dCoastNormalLength / m_dCellSide); // Profile length in grid CRS
509 int nProfileStartEdge;
510
511 CGeom2DIPoint PtiProfileStart; // In grid CRS
512 vector<CGeom2DIPoint> VPtiNormalPoints; // In grid CRS
513
514 if (bCoastStart)
515 {
516 // At start of coast
517 PtiProfileStart = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(0); // Grid CRS
518 nProfileStartEdge = m_VCoast[nCoast].nGetStartEdge();
519 }
520 else
521 {
522 // At end of coast
523 PtiProfileStart = *m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nCoastSize - 1); // Grid CRS
524 nProfileStartEdge = m_VCoast[nCoast].nGetEndEdge();
525 }
526
527 VPtiNormalPoints.push_back(PtiProfileStart);
528
529 // Find the start cell in the list of edge cells
530 auto it = find(m_VEdgeCell.begin(), m_VEdgeCell.end(), PtiProfileStart);
531 if (it == m_VEdgeCell.end())
532 {
533 // Not found. This can happen because of rounding problems, i.e. the cell which was stored as the first cell of the raster coastline
535 LogStream << m_ulIter << ": " << ERR << " when constructing start-of-coast profile, [" << PtiProfileStart.nGetX() << "][" << PtiProfileStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiProfileStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiProfileStart.nGetY()) << "} not found in list of edge cells" << endl;
536
538 }
539
540 // Found
541 int nPos = static_cast<int>(it - m_VEdgeCell.begin());
542
543 // Now construct the edge profile, searching for edge cells
544 for (int n = 0; n < nProfileLen; n++)
545 {
546 if (bCoastStart)
547 {
548 // At start of coast
549 if (nHandedness == LEFT_HANDED)
550 {
551 // The list of edge cells is in clockwise sequence, go in this direction
552 nPos++;
553
554 if (nPos >= static_cast<int>(m_VEdgeCell.size()))
555 {
556 // We've reached the end of the list of edge cells before the profile is long enough. OK, we can live with this
557 break;
558 }
559 }
560 else // Right-handed
561 {
562 // The list of edge cells is in clockwise sequence, go in the opposite direction
563 nPos--;
564
565 if (nPos < 0)
566 {
567 // We've reached the beginning of the list of edge cells before the profile is long enough. OK, we can live with this
568 break;
569 }
570 }
571 }
572 else
573 {
574 // At end of coast
575 if (nHandedness == LEFT_HANDED)
576 {
577 // The list of edge cells is in clockwise sequence, go in the opposite direction
578 nPos--;
579
580 if (nPos < 0)
581 {
582 // We've reached the beginning of the list of edge cells before the profile is long enough. OK, we can live with this
583 break;
584 }
585 }
586 else // Right-handed
587 {
588 // The list of edge cells is in clockwise sequence, go in this direction
589 nPos++;
590
591 if (nPos >= static_cast<int>(m_VEdgeCell.size()))
592 {
593 // We've reached the end of the list of edge cells before the profile is long enough. OK, we can live with this
594 break;
595 }
596 }
597 }
598
599 if (m_VEdgeCellEdge[nPos] != nProfileStartEdge)
600 {
601 // We've reached the end of a grid side before the profile is long enough. OK, we can live with this
602 break;
603 }
604
605 // Append this grid-edge cell, making sure that there is no gap between this and the previously-appended cell (if there is, will get problems with flood fill)
606 AppendEnsureNoGap(&VPtiNormalPoints, &m_VEdgeCell[nPos]);
607 }
608
609 int nProfileStartPoint;
610 CGeomProfile* pProfile;
612 if (bCoastStart)
613 {
614 nProfileStartPoint = 0;
615
616 // Create the new start-of-coast profile
617 pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, ++nProfileGlobalID, &PtiProfileStart, &PtiDummy, false);
618
619 // Mark this as a start-of-coast profile
620 pProfile->SetStartOfCoast(true);
621 }
622 else
623 {
624 nProfileStartPoint = nCoastSize-1;
625
626 // Create the new end-of-coast profile
627 pProfile = new CGeomProfile(nCoast, nProfileStartPoint, nProfile, ++nProfileGlobalID, &PtiProfileStart, &PtiDummy, false);
628
629 // Mark this as an end-of-coast profile
630 pProfile->SetEndOfCoast(true);
631 }
632
633 // Create the list of cells 'under' this grid-edge profile. Note that more than two cells are stored
634 for (unsigned int n = 0; n < VPtiNormalPoints.size(); n++)
635 {
636 int nX = VPtiNormalPoints[n].nGetX();
637 int nY = VPtiNormalPoints[n].nGetY();
638
639 // Mark each cell in the raster grid
640 m_pRasterGrid->m_Cell[nX][nY].SetProfileID(nProfile);
641
642 // Store the raster grid coordinates in the profile object
643 pProfile->AppendCellInProfile(nX, nY);
644
645 CGeom2DPoint Pt(dGridCentroidXToExtCRSX(nX), dGridCentroidYToExtCRSY(nY)); // In external CRS
646
647 // Store the external coordinates in the profile object. Note that for this 'special' profile, the coordinates of the cells and the coordinates of points on the profile itself are identical, this is not the case for ordinary profiles
648 pProfile->AppendCellInProfileExtCRS(&Pt);
649
650 if ((n == 0) || (n == VPtiNormalPoints.size()-1))
651 {
652 // Store just the first and last points in this 'special' profile's coastline-normal vector
653 pProfile->AppendPointInProfile(&Pt);
654 }
655 }
656
657 int nEndX = VPtiNormalPoints.back().nGetX();
658 int nEndY = VPtiNormalPoints.back().nGetY();
659 CGeom2DIPoint PtiEnd(nEndX, nEndY);
660 pProfile->SetEndPoint(&PtiEnd);
661
662 // Get the deep water wave height and orientation values at the end of the profile
663 double dDeepWaterWaveHeight = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWaveHeight();
664 double dDeepWaterWaveAngle = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWaveAngle();
665 double dDeepWaterWavePeriod = m_pRasterGrid->m_Cell[nEndX][nEndY].dGetCellDeepWaterWavePeriod();
666
667 // And store them in this profile
668 pProfile->SetProfileDeepWaterWaveHeight(dDeepWaterWaveHeight);
669 pProfile->SetProfileDeepWaterWaveAngle(dDeepWaterWaveAngle);
670 pProfile->SetProfileDeepWaterWavePeriod(dDeepWaterWavePeriod);
671
672 // Create the profile's CGeomMultiLine then set nProfile as the only co-incident profile of the only line segment
673 pProfile->AppendLineSegment();
674 pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0));
675
676 // Store the profile
677 m_VCoast[nCoast].AppendProfile(pProfile);
678 m_VCoast[nCoast].SetProfileAtCoastPoint(nProfileStartPoint, pProfile);
679
681 LogStream << m_ulIter << ": coast " << nCoast << " profile " << nProfile << " created at coast " << (bCoastStart ? "start" : "end") << " point " << (bCoastStart ? 0 : nCoastSize - 1) << ", from [" << PtiProfileStart.nGetX() << "][" << PtiProfileStart.nGetY() << "] = {" << dGridCentroidXToExtCRSX(PtiProfileStart.nGetX()) << ", " << dGridCentroidYToExtCRSY(PtiProfileStart.nGetY()) << "} to [" << VPtiNormalPoints.back().nGetX() << "][" << VPtiNormalPoints.back().nGetY() << "] = {" << dGridCentroidXToExtCRSX(VPtiNormalPoints.back().nGetX()) << ", " << dGridCentroidYToExtCRSY(VPtiNormalPoints.back().nGetY()) << "}" << endl;
682
683 return RTN_OK;
684}
685
686//===============================================================================================================================
688//===============================================================================================================================
689int CSimulation::nGetCoastNormalEndPoint(int const nCoast, int const nStartCoastPoint, int const nCoastSize, CGeom2DPoint const* pPtStart, double const dLineLength, CGeom2DPoint* pPtEnd, CGeom2DIPoint* pPtiEnd, bool const bIntervention)
690{
691 int const AVGSIZE = 21; // TODO 011 This should be a user input
692
693 CGeom2DPoint PtBefore;
694 CGeom2DPoint PtAfter;
695
696 if (bIntervention)
697 {
698 // This is an intervention profile, so just use one point on either side (coordinates in external CRS). Note this this assumes that this intervention profile is not at the start or end of the coastline
699 PtBefore = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nStartCoastPoint-1);
700 PtAfter = *m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nStartCoastPoint+1);
701 }
702 else
703 {
704 // This is not an intervention, so put a maximum of AVGSIZE points before the start point into a vector
705 vector<CGeom2DPoint> PtBeforeToAverage;
706 for (int n = 1; n <= AVGSIZE; n++)
707 {
708 int nPoint = nStartCoastPoint - n;
709 if (nPoint < 0)
710 break;
711
712 PtBeforeToAverage.push_back(*m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nPoint));
713 }
714
715 // Put a maximum of AVGSIZE points after the start point into a vector
716 vector<CGeom2DPoint> PtAfterToAverage;
717 for (int n = 1; n <= AVGSIZE; n++)
718 {
719 int nPoint = nStartCoastPoint + n;
720 if (nPoint > nCoastSize-1)
721 break;
722
723 PtAfterToAverage.push_back(*m_VCoast[nCoast].pPtGetCoastlinePointExtCRS(nPoint));
724 }
725
726 // Now average each of these vectors of points: results are in PtBefore and PtAfter (coordinates in external CRS)
727 PtBefore = PtAverage(&PtBeforeToAverage);
728 PtAfter = PtAverage(&PtAfterToAverage);
729 }
730
731 // Get the y = a * x + b equation of the straight line linking the coastline points before and after 'this' coastline point. For this linking line, slope a = (y2 - y1) / (x2 - x1)
732 double dYDiff = PtAfter.dGetY() - PtBefore.dGetY();
733 double dXDiff = PtAfter.dGetX() - PtBefore.dGetX();
734
735 double dXEnd1 = 0, dXEnd2 = 0, dYEnd1 = 0, dYEnd2 = 0;
736
737 if (bFPIsEqual(dYDiff, 0.0, TOLERANCE))
738 {
739 // The linking line runs W-E or E-W, so a straight line at right angles to this runs N-S or S-N. Calculate the two possible end points for this coastline-normal profile
740 dXEnd1 = dXEnd2 = pPtStart->dGetX();
741 dYEnd1 = pPtStart->dGetY() + dLineLength;
742 dYEnd2 = pPtStart->dGetY() - dLineLength;
743 }
744 else if (bFPIsEqual(dXDiff, 0.0, TOLERANCE))
745 {
746 // The linking line runs N-S or S-N, so a straight line at right angles to this runs W-E or E-W. Calculate the two possible end points for this coastline-normal profile
747 dYEnd1 = dYEnd2 = pPtStart->dGetY();
748 dXEnd1 = pPtStart->dGetX() + dLineLength;
749 dXEnd2 = pPtStart->dGetX() - dLineLength;
750 }
751 else
752 {
753 // The linking line runs neither W-E nor N-S so we have to work a bit harder to find the end-point of the coastline-normal profile
754 double dA = dYDiff / dXDiff;
755
756 // Now calculate the equation of the straight line which is perpendicular to this linking line
757 double dAPerp = -1 / dA;
758 double dBPerp = pPtStart->dGetY() - (dAPerp * pPtStart->dGetX());
759
760 // Calculate the end point of the profile: first do some substitution then rearrange as a quadratic equation i.e. in the form Ax^2 + Bx + C = 0 (see http://math.stackexchange.com/questions/228841/how-do-i-calculate-the-intersections-of-a-straight-line-and-a-circle)
761 double dQuadA = 1 + (dAPerp * dAPerp);
762 double dQuadB = 2 * ((dBPerp * dAPerp) - (dAPerp * pPtStart->dGetY()) - pPtStart->dGetX());
763 double dQuadC = ((pPtStart->dGetX() * pPtStart->dGetX()) + (pPtStart->dGetY() * pPtStart->dGetY()) + (dBPerp * dBPerp) - (2 * pPtStart->dGetY() * dBPerp) - (dLineLength * dLineLength));
764
765 // Solve for x and y using the quadratic formula x = (−B ± sqrt(B^2 − 4AC)) / 2A
766 double dDiscriminant = (dQuadB * dQuadB) - (4 * dQuadA * dQuadC);
767 if (dDiscriminant < 0)
768 {
769 LogStream << ERR << "timestep " << m_ulIter << ": discriminant < 0 when finding profile end point on coastline " << nCoast << ", from coastline point " << nStartCoastPoint << "), ignored" << endl;
771 }
772
773 dXEnd1 = (-dQuadB + sqrt(dDiscriminant)) / (2 * dQuadA);
774 dYEnd1 = (dAPerp * dXEnd1) + dBPerp;
775 dXEnd2 = (-dQuadB - sqrt(dDiscriminant)) / (2 * dQuadA);
776 dYEnd2 = (dAPerp * dXEnd2) + dBPerp;
777 }
778
779 // We have two possible solutions, so decide which of the two endpoints to use then create the profile end-point (coordinates in external CRS)
780 int nSeaHand = m_VCoast[nCoast].nGetSeaHandedness(); // Assumes handedness is either 0 or 1 (i.e. not -1)
781 *pPtEnd = PtChooseEndPoint(nSeaHand, &PtBefore, &PtAfter, dXEnd1, dYEnd1, dXEnd2, dYEnd2);
782
783 // Check that pPtiEnd is not off the grid. Note that pPtiEnd is not necessarily a cell centroid
784 pPtiEnd->SetXY(nRound(dExtCRSXToGridX(pPtEnd->dGetX())), nRound(dExtCRSYToGridY(pPtEnd->dGetY())));
785 if (! bIsWithinValidGrid(pPtiEnd))
786 {
787 // The end point is off the grid, so constrain it to be within the valid grid
788 CGeom2DIPoint PtiStart(nRound(dExtCRSXToGridX(pPtStart->dGetX())), nRound(dExtCRSYToGridY(pPtStart->dGetY())));
789 KeepWithinValidGrid(&PtiStart, pPtiEnd);
790
791 pPtEnd->SetX(dGridCentroidXToExtCRSX(pPtiEnd->nGetX()));
792 pPtEnd->SetY(dGridCentroidYToExtCRSY(pPtiEnd->nGetY()));
793
794 // LogStream << m_ulIter << ": changed endpoint for profile, is now [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}. The profile starts at coastline point " << nStartCoastPoint << " = {" << pPtStart->dGetX() << ", " << pPtStart->dGetY() << "}" << endl;
795
797 }
798
799 return RTN_OK;
800}
801
802//===============================================================================================================================
804//===============================================================================================================================
805CGeom2DPoint CSimulation::PtChooseEndPoint(int const nHand, CGeom2DPoint const *PtBefore, CGeom2DPoint const *PtAfter, double const dXEnd1, double const dYEnd1, double const dXEnd2, double const dYEnd2)
806{
807 CGeom2DPoint PtChosen;
808
809 // All coordinates here are in the external CRS, so the origin of the grid is the bottom left
810 if (nHand == RIGHT_HANDED)
811 {
812 // The sea is to the right of the linking line. So which way is the linking line oriented? First check the N-S component
813 if (PtAfter->dGetY() > PtBefore->dGetY())
814 {
815 // We are going S to N and the sea is to the right: the normal endpoint is to the E. We want the larger of the two x values
816 if (dXEnd1 > dXEnd2)
817 {
818 PtChosen.SetX(dXEnd1);
819 PtChosen.SetY(dYEnd1);
820 }
821 else
822 {
823 PtChosen.SetX(dXEnd2);
824 PtChosen.SetY(dYEnd2);
825 }
826 }
827 else if (PtAfter->dGetY() < PtBefore->dGetY())
828 {
829 // We are going N to S and the sea is to the right: the normal endpoint is to the W. We want the smaller of the two x values
830 if (dXEnd1 < dXEnd2)
831 {
832 PtChosen.SetX(dXEnd1);
833 PtChosen.SetY(dYEnd1);
834 }
835 else
836 {
837 PtChosen.SetX(dXEnd2);
838 PtChosen.SetY(dYEnd2);
839 }
840 }
841 else
842 {
843 // No N-S component i.e. the linking line is exactly W-E. So check the W-E component
844 if (PtAfter->dGetX() > PtBefore->dGetX())
845 {
846 // We are going W to E and the sea is to the right: the normal endpoint is to the s. We want the smaller of the two y values
847 if (dYEnd1 < dYEnd2)
848 {
849 PtChosen.SetX(dXEnd1);
850 PtChosen.SetY(dYEnd1);
851 }
852 else
853 {
854 PtChosen.SetX(dXEnd2);
855 PtChosen.SetY(dYEnd2);
856 }
857 }
858 else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident
859 {
860 // We are going E to W and the sea is to the right: the normal endpoint is to the N. We want the larger of the two y values
861 if (dYEnd1 > dYEnd2)
862 {
863 PtChosen.SetX(dXEnd1);
864 PtChosen.SetY(dYEnd1);
865 }
866 else
867 {
868 PtChosen.SetX(dXEnd2);
869 PtChosen.SetY(dYEnd2);
870 }
871 }
872 }
873 }
874 else // nHand == LEFT_HANDED
875 {
876 // The sea is to the left of the linking line. So which way is the linking line oriented? First check the N-S component
877 if (PtAfter->dGetY() > PtBefore->dGetY())
878 {
879 // We are going S to N and the sea is to the left: the normal endpoint is to the W. We want the smaller of the two x values
880 if (dXEnd1 < dXEnd2)
881 {
882 PtChosen.SetX(dXEnd1);
883 PtChosen.SetY(dYEnd1);
884 }
885 else
886 {
887 PtChosen.SetX(dXEnd2);
888 PtChosen.SetY(dYEnd2);
889 }
890 }
891 else if (PtAfter->dGetY() < PtBefore->dGetY())
892 {
893 // We are going N to S and the sea is to the left: the normal endpoint is to the E. We want the larger of the two x values
894 if (dXEnd1 > dXEnd2)
895 {
896 PtChosen.SetX(dXEnd1);
897 PtChosen.SetY(dYEnd1);
898 }
899 else
900 {
901 PtChosen.SetX(dXEnd2);
902 PtChosen.SetY(dYEnd2);
903 }
904 }
905 else
906 {
907 // No N-S component i.e. the linking line is exactly W-E. So check the W-E component
908 if (PtAfter->dGetX() > PtBefore->dGetX())
909 {
910 // We are going W to E and the sea is to the left: the normal endpoint is to the N. We want the larger of the two y values
911 if (dYEnd1 > dYEnd2)
912 {
913 PtChosen.SetX(dXEnd1);
914 PtChosen.SetY(dYEnd1);
915 }
916 else
917 {
918 PtChosen.SetX(dXEnd2);
919 PtChosen.SetY(dYEnd2);
920 }
921 }
922 else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident
923 {
924 // We are going E to W and the sea is to the left: the normal endpoint is to the S. We want the smaller of the two y values
925 if (dYEnd1 < dYEnd2)
926 {
927 PtChosen.SetX(dXEnd1);
928 PtChosen.SetY(dYEnd1);
929 }
930 else
931 {
932 PtChosen.SetX(dXEnd2);
933 PtChosen.SetY(dYEnd2);
934 }
935 }
936 }
937 }
938
939 return PtChosen;
940}
941
942//===============================================================================================================================
944//===============================================================================================================================
946{
947 int nMaxDist = static_cast<int>(m_dCoastNormalAvgSpacing) * PROFILE_CHECK_ALONG_COAST_MULTIPLIER;
948
949 // Do once for every coastline object
950 int nCoastLines = static_cast<int>(m_VCoast.size());
951 for (int nCoast = 0; nCoast < nCoastLines; nCoast++)
952 {
953 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
954
955 // Do once for every profile, in along-coast sequence
956 for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++)
957 {
958 if (! m_VCoast[nCoast].bIsProfileAtCoastPoint(nCoastPoint))
959 continue;
960
961 // There is a profile at this coast point
962 CGeomProfile* pFirstProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nCoastPoint);
963 int nFirstProfile = pFirstProfile->nGetCoastID();
964
965 // Only check this profile if it is problem free, and is not a start- or end-of-coast profile. Continue checking if it has been truncated, however
966 if (! pFirstProfile->bProfileOKIncTruncated())
967 continue;
968
969 // Don't modify the start- or end-of coastline normals
970 if ((pFirstProfile->bStartOfCoast()) || (pFirstProfile->bEndOfCoast()))
971 continue;
972
973 // OK we have found a first profile. Now go along the coast, looking at profiles which are increasingly distant from the first profile
974 for (int nDist = 1; nDist < nMaxDist; nDist++)
975 {
976 // Do this in alternate directions: first down-coast (in the direction of increasing coast point numbers) then up-coast
977 for (int nDirection = DIRECTION_DOWNCOAST; nDirection <= DIRECTION_UPCOAST; nDirection++)
978 {
979 int nSecondCoastPoint;
980 if (nDirection == DIRECTION_DOWNCOAST)
981 nSecondCoastPoint = nCoastPoint + nDist;
982 else
983 nSecondCoastPoint = nCoastPoint - nDist;
984
985 if ((nSecondCoastPoint < 0) || (nSecondCoastPoint > nCoastSize-1))
986 // Make sure that we don't go beyond the ends of the coast
987 continue;
988
989 if (m_VCoast[nCoast].bIsProfileAtCoastPoint(nSecondCoastPoint))
990 {
991 // There is a profile at the second coast point, so get a pointer to it
992 CGeomProfile* pSecondProfile = m_VCoast[nCoast].pGetProfileAtCoastPoint(nSecondCoastPoint);
993 int nSecondProfile = pSecondProfile->nGetCoastID();
994
995 // // DEBUG CODE ===============================
996 // if ((nFirstProfile == 26) && (nSecondProfile == 27))
997 // cerr << endl;
998 // // DEBUG CODE ===============================
999
1000 // LogStream << m_ulIter << ": " << (nDirection == DIRECTION_DOWNCOAST ? "down" : "up") << "-coast search, nCoastPoint = " << nCoastPoint << " nSecondCoastPoint = " << nSecondCoastPoint << " (profiles " << pFirstProfile->nGetCoastID() << " and " << pSecondProfile->nGetCoastID() << ")" << endl;
1001
1002 // Only check this profile if it is problem free, and is not a start- or end-of-coast profile. Continue checking if it has been truncated, however
1003 if (! pSecondProfile->bProfileOKIncTruncated())
1004 continue;
1005
1006 // Only check these two profiles for intersection if they are are not co-incident in the final line segment of both profiles (i.e. the profiles have not already intersected)
1007 if ((pFirstProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nSecondProfile)) || (pSecondProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nFirstProfile)))
1008 continue;
1009
1010 // OK go for it
1011 int nProf1LineSeg = 0;
1012 int nProf2LineSeg = 0;
1013 double dIntersectX = 0;
1014 double dIntersectY = 0;
1015 double dAvgEndX = 0;
1016 double dAvgEndY = 0;
1017
1018 if (bCheckForIntersection(pFirstProfile, pSecondProfile, nProf1LineSeg, nProf2LineSeg, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY))
1019 {
1020 // The profiles intersect. Decide which profile to truncate, and which to retain
1021 int nPoint = -1;
1022
1023 if (pFirstProfile->bIsIntervention())
1024 // Truncate the first profile, since it is an intervention profile
1025 TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false);
1026
1027 else if (pSecondProfile->bIsIntervention())
1028 // Truncate the second profile, ssince it is an intervention profile
1029 TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false);
1030
1031 // Is the point of intersection already present in the first profile (i.e. because there has already been an intersection at this point between the first profile and some other profile)?
1032 else if (pFirstProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint))
1033 {
1034 // LogStream << m_ulIter << ": profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nFirstProfile << " as point " << nPoint << endl;
1035
1036 // Truncate the second profile and merge it with the first profile
1037 TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, true);
1038 }
1039
1040 // Is the point of intersection already present in the second profile?
1041 else if (pSecondProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint))
1042 {
1043 // LogStream << m_ulIter << ": profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nSecondProfile << " as point " << nPoint << endl;
1044
1045 // Truncate the first profile and merge it with the second profile
1046 TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, true);
1047 }
1048
1049 else
1050 {
1051 // The point of intersection is not already present in either profile, so get the number of line segments of each profile
1052 int nFirstProfileLineSegments = pFirstProfile->nGetNumLineSegments();
1053 int nSecondProfileLineSegments = pSecondProfile->nGetNumLineSegments();
1054
1055 // assert(nProf1LineSeg < nFirstProfileLineSegments);
1056 // assert(nProf2LineSeg < nSecondProfileLineSegments);
1057
1058 // Next check whether the point of intersection is on the final line segment of both profiles
1059 if ((nProf1LineSeg == (nFirstProfileLineSegments - 1)) && (nProf2LineSeg == (nSecondProfileLineSegments - 1)))
1060 {
1061 // Yes, the point of intersection is on the final line segment of both profiles, so merge the profiles seaward of the point of intersection
1062 MergeProfilesAtFinalLineSegments(nCoast, pFirstProfile, pSecondProfile, nFirstProfileLineSegments, nSecondProfileLineSegments, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY);
1063
1064 // LogStream << m_ulIter << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, end-segment intersection between profiles " << nFirstProfile << " and " << nSecondProfile << " at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << " segments, and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << " segments, respectively" << endl;
1065
1066 // // DEBUG CODE =============================================================================================
1067 // int nSizeTmp = pFirstProfile->nGetProfileSize();
1068 // CGeom2DPoint PtEndTmp = *pFirstProfile->pPtGetPointInProfile(nSizeTmp-1);
1069 //
1070 // LogStream << m_ulIter << ": end of first profile (" << nFirstProfile << ") is point " << nSizeTmp-1 << " at [" << dExtCRSXToGridX(PtEndTmp.dGetX()) << "][" << dExtCRSYToGridY(PtEndTmp.dGetY()) << "} = {" << PtEndTmp.dGetX() << ", " << PtEndTmp.dGetY() << "}" << endl;
1071 //
1072 // nSizeTmp = pSecondProfile->nGetProfileSize();
1073 // PtEndTmp = *pSecondProfile->pPtGetPointInProfile(nSizeTmp-1);
1074 //
1075 // LogStream << m_ulIter << ": end of second profile (" << nSecondProfile << ") is point " << nSizeTmp-1 << " at [" << dExtCRSXToGridX(PtEndTmp.dGetX()) << "][" << dExtCRSYToGridY(PtEndTmp.dGetY()) << "} = {" << PtEndTmp.dGetX() << ", " << PtEndTmp.dGetY() << "}" << endl;
1076 // // DEBUG CODE =============================================================================================
1077 }
1078 else
1079 {
1080 // The profiles intersect, but the point of intersection is not on the final line segment of both profiles. One of the profiles will be truncated, the other profile will be retained
1081 // LogStream << m_ulIter << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, intersection (NOT both end segments) between profiles " << nFirstProfile << " and " << nSecondProfile << " at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << ", and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << ", respectively" << endl;
1082
1083 // Decide which profile to truncate, and which to retain
1084 if (pFirstProfile->bIsIntervention())
1085 // Truncate the first profile, since it is an intervention profile
1086 TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false);
1087
1088 else if (pSecondProfile->bIsIntervention())
1089 // Truncate the second profile, ssince it is an intervention profile
1090 TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false);
1091
1092 else if (nFirstProfileLineSegments > nSecondProfileLineSegments)
1093 // Truncate the second profile, since it has a smaller number of line segments
1094 TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false);
1095
1096 else if (nFirstProfileLineSegments < nSecondProfileLineSegments)
1097 // Truncate the first profile, since it has a smaller number of line segments
1098 TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false);
1099
1100 else
1101 {
1102 // Both profiles have the same number of line segments, so choose randomly. Draw a sample from the unit normal distribution using random number generator 1
1103 double dRand = m_dUnitNormalDist(m_Rand[0]);
1104 if (dRand >= 0.0)
1105 TruncateOneProfileRetainOtherProfile(nCoast, pFirstProfile, pSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false);
1106 else
1107 TruncateOneProfileRetainOtherProfile(nCoast, pSecondProfile, pFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false);
1108 }
1109 }
1110 }
1111
1112 // int
1113 // nProfile1NumSegments = pFirstProfile->nGetNumLineSegments(),
1114 // nProfile2NumSegments = pSecondProfile->nGetNumLineSegments(),
1115 // nProfile1Size = pFirstProfile->nGetProfileSize(),
1116 // nProfile2Size = pSecondProfile->nGetProfileSize();
1117 //
1118 // assert(pFirstProfile->nGetNumLineSegments() > 0);
1119 // assert(pSecondProfile->nGetNumLineSegments() > 0);
1120 // assert(nProfile1Size == nProfile1NumSegments+1);
1121 // assert(nProfile2Size == nProfile2NumSegments+1);
1122 }
1123 }
1124 }
1125 }
1126 }
1127 }
1128}
1129
1130//===============================================================================================================================
1132//===============================================================================================================================
1134{
1135 // Check to see which coastline-normal profiles intersect. Then modify intersecting profiles so that the sections of each profile seaward of the point of intersection are 'shared' i.e. are multi-lines. This creates the boundaries of the triangular polygons
1137
1138 // Again check the normal profiles for insufficient length: is the water depth at the end point less than the depth of closure? We do this again because some profiles may have been shortened as a result of intersection. Do once for every coastline object
1139 for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++)
1140 {
1141 for (int n = 0; n < m_VCoast[nCoast].nGetNumProfiles(); n++)
1142 {
1143 CGeomProfile* pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n);
1144 int nProfile = pProfile->nGetCoastID();
1145
1146 if (pProfile->bProfileOK())
1147 {
1148 int nSize = pProfile->nGetProfileSize();
1149
1150 // Safety check
1151 if (nSize == 0)
1152 {
1153 // pProfile->SetTooShort(true);
1154 m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true);
1155 LogStream << "Profile " << nProfile << " is too short, size = " << nSize << endl;
1156 continue;
1157 }
1158
1159 CGeom2DPoint const* pPtEnd = pProfile->pPtGetPointInProfile(nSize - 1);
1160 CGeom2DIPoint PtiEnd = PtiExtCRSToGridRound(pPtEnd);
1161 int nXEnd = PtiEnd.nGetX();
1162 int nYEnd = PtiEnd.nGetY();
1163
1164 // Safety check: the point may be outside the grid in the +VE direction, so keep it within the grid
1165 nXEnd = tMin(nXEnd, m_nXGridSize - 1);
1166 nYEnd = tMin(nYEnd, m_nYGridSize - 1);
1167
1168 // pProfile->SetEndPoint(&PtiEnd);
1169 m_VCoast[nCoast].pGetProfile(nProfile)->SetEndPoint(&PtiEnd);
1170
1171 if (m_pRasterGrid->m_Cell[nXEnd][nYEnd].dGetSeaDepth() < m_dDepthOfClosure)
1172 {
1174 LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, is too short for depth of closure " << m_dDepthOfClosure << " at end point [" << nXEnd << "][" << nYEnd << "] = {" << pPtEnd->dGetX() << ", " << pPtEnd->dGetY() << "}, flagging as too short" << endl;
1175
1176 // pProfile->SetTooShort(true);
1177 m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true);
1178 }
1179 }
1180 }
1181
1182 // For this coast, put all valid coastline-normal profiles (apart from the profiles at the start and end of the coast, since they have already been done) onto the raster grid. But if the profile is not long enough, or crosses a coastline or hits dry land, then mark the profile as invalid
1183 int nValidProfiles = 0;
1184 MarkProfilesOnGrid(nCoast, nValidProfiles);
1185
1186 if (nValidProfiles == 0)
1187 {
1188 // Problem! No valid profiles, so quit
1189 cerr << m_ulIter << ": " << ERR << "no coastline-normal profiles created" << endl;
1190 return RTN_ERR_NO_PROFILES_2;
1191 }
1192
1193 // // DEBUG CODE ===========================================================================================================
1194 // if (m_ulIter == 109)
1195 // {
1196 // string strOutFile = m_strOutPath;
1197 // strOutFile += "00_profile_raster_";
1198 // strOutFile += to_string(m_ulIter);
1199 // strOutFile += ".tif";
1200 //
1201 // GDALDriver* pDriver = GetGDALDriverManager()->GetDriverByName("gtiff");
1202 // GDALDataset* pDataSet = pDriver->Create(strOutFile.c_str(), m_nXGridSize, m_nYGridSize, 1, GDT_Float64, m_papszGDALRasterOptions);
1203 // pDataSet->SetProjection(m_strGDALBasementDEMProjection.c_str());
1204 // pDataSet->SetGeoTransform(m_dGeoTransform);
1205 //
1206 // int nn = 0;
1207 // double* pdRaster = new double[m_nXGridSize * m_nYGridSize];
1208 // for (int nY = 0; nY < m_nYGridSize; nY++)
1209 // {
1210 // for (int nX = 0; nX < m_nXGridSize; nX++)
1211 // {
1212 // if (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline())
1213 // pdRaster[nn] = -1;
1214 // else
1215 // {
1216 //
1217 // // pdRaster[nn] = m_pRasterGrid->m_Cell[nX][nY].nGetPolygonID();
1218 // pdRaster[nn] = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID();
1219 // }
1220 //
1221 // nn++;
1222 // }
1223 // }
1224 //
1225 // GDALRasterBand* pBand = pDataSet->GetRasterBand(1);
1226 // pBand->SetNoDataValue(m_dMissingValue);
1227 // int nRet1 = pBand->RasterIO(GF_Write, 0, 0, m_nXGridSize, m_nYGridSize, pdRaster, m_nXGridSize, m_nYGridSize, GDT_Float64, 0, 0, NULL);
1228 //
1229 // if (nRet1 == CE_Failure)
1230 // return RTN_ERR_GRIDCREATE;
1231 //
1232 // GDALClose(pDataSet);
1233 // delete[] pdRaster;
1234 // }
1235 // // DEBUG CODE ===========================================================================================================
1236 }
1237
1238 return RTN_OK;
1239}
1240
1241//===============================================================================================================================
1243//===============================================================================================================================
1244bool CSimulation::bCheckForIntersection(CGeomProfile* const pVProfile1, CGeomProfile* const pVProfile2, int&nProfile1LineSegment, int& nProfile2LineSegment, double& dXIntersect, double& dYIntersect, double& dXAvgEnd, double& dYAvgEnd)
1245{
1246 // For both profiles, look at all line segments
1247 int nProfile1NumSegments = pVProfile1->nGetNumLineSegments();
1248 int nProfile2NumSegments = pVProfile2->nGetNumLineSegments();
1249 // nProfile1Size = pVProfile1->nGetProfileSize(),
1250 // nProfile2Size = pVProfile2->nGetProfileSize();
1251
1252 // assert(nProfile1Size == nProfile1NumSegments+1);
1253 // assert(nProfile2Size == nProfile2NumSegments+1);
1254
1255 for (int i = 0; i < nProfile1NumSegments; i++)
1256 {
1257 for (int j = 0; j < nProfile2NumSegments; j++)
1258 {
1259 // In external coordinates
1260 double dX1 = pVProfile1->pPtVGetPoints()->at(i).dGetX();
1261 double dY1 = pVProfile1->pPtVGetPoints()->at(i).dGetY();
1262 double dX2 = pVProfile1->pPtVGetPoints()->at(i + 1).dGetX();
1263 double dY2 = pVProfile1->pPtVGetPoints()->at(i + 1).dGetY();
1264
1265 double dX3 = pVProfile2->pPtVGetPoints()->at(j).dGetX();
1266 double dY3 = pVProfile2->pPtVGetPoints()->at(j).dGetY();
1267 double dX4 = pVProfile2->pPtVGetPoints()->at(j + 1).dGetX();
1268 double dY4 = pVProfile2->pPtVGetPoints()->at(j + 1).dGetY();
1269
1270 // Uses Cramer's Rule to solve the equations. Modified from code at http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect (in turn based on Andre LeMothe's "Tricks of the Windows Game Programming Gurus")
1271 double dDiffX1 = dX2 - dX1;
1272 double dDiffY1 = dY2 - dY1;
1273 double dDiffX2 = dX4 - dX3;
1274 double dDiffY2 = dY4 - dY3;
1275
1276 double dS = -999;
1277 double dT = -999;
1278 double dTmp = 0;
1279
1280 dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2;
1281 if (! bFPIsEqual(dTmp, 0.0, TOLERANCE))
1282 dS = (-dDiffY1 * (dX1 - dX3) + dDiffX1 * (dY1 - dY3)) / dTmp;
1283
1284 dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2;
1285 if (! bFPIsEqual(dTmp, 0.0, TOLERANCE))
1286 dT = (dDiffX2 * (dY1 - dY3) - dDiffY2 * (dX1 - dX3)) / dTmp;
1287
1288 if (dS >= 0 && dS <= 1 && dT >= 0 && dT <= 1)
1289 {
1290 // Collision detected, calculate intersection coordinates
1291 dXIntersect = dX1 + (dT * dDiffX1);
1292 dYIntersect = dY1 + (dT * dDiffY1);
1293
1294 // And calc the average end-point coordinates
1295 dXAvgEnd = (dX2 + dX4) / 2;
1296 dYAvgEnd = (dY2 + dY4) / 2;
1297
1298 // Get the line segments at which intersection occurred
1299 nProfile1LineSegment = i;
1300 nProfile2LineSegment = j;
1301
1302 // LogStream << "\t" << "INTERSECTION dX2 = " << dX2 << " dX4 = " << dX4 << " dY2 = " << dY2 << " dY4 = " << dY4 << endl;
1303 return true;
1304 }
1305 }
1306 }
1307
1308 // No collision
1309 return false;
1310}
1311
1312//===============================================================================================================================
1314//===============================================================================================================================
1315void CSimulation::MarkProfilesOnGrid(int const nCoast, int& nValidProfiles)
1316{
1317 // How many profiles on this coast?
1318 int nProfiles = m_VCoast[nCoast].nGetNumProfiles();
1319 if (nProfiles == 0)
1320 {
1321 // This can happen if the coastline is very short, so just give a warning and carry on with the next coastline
1323 LogStream << WARN << m_ulIter << ": coast " << nCoast << " has no profiles" << endl;
1324
1325 return;
1326 }
1327
1328 static bool bDownCoast = true;
1329
1330 // Now do this for every profile, alternate between up-coast and down-coast directions
1331 for (int n = 0; n < nProfiles; n++)
1332 {
1333 CGeomProfile* pProfile;
1334
1335 if (bDownCoast)
1336 pProfile = m_VCoast[nCoast].pGetProfileWithDownCoastSeq(n);
1337 else
1338 pProfile = m_VCoast[nCoast].pGetProfileWithUpCoastSeq(n);
1339
1340 // Don't do this for the first and last profiles (i.e. the profiles at the start and end of the coast) since these are put onto the grid elsewhere
1341 if ((pProfile->bStartOfCoast()) || (pProfile->bEndOfCoast()))
1342 continue;
1343
1344 int nProfile = pProfile->nGetCoastID();
1345
1346 // If this profile has a problem, then forget about it
1347 // if (! pProfile->bProfileOK())
1348 // {
1349 // LogStream << m_ulIter << ": in MarkProfilesOnGrid() profile " << nProfile << " is not OK" << endl;
1350 // continue;
1351 // }
1352
1353 int nPoints = pProfile->nGetProfileSize();
1354 if (nPoints < 2)
1355 {
1356 // Need at least two points in the profile, so this profile is invalid: mark it
1357 m_VCoast[nCoast].pGetProfile(nProfile)->SetTooShort(true);
1358
1360 LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, has only " << nPoints << " points" << endl;
1361
1362 continue;
1363 }
1364
1365 // OK, go for it: set up temporary vectors to hold the x-y coords (in grid CRS) of the cells which we will mark
1366 vector<CGeom2DIPoint> VCellsToMark;
1367 vector<bool> bVShared;
1368 bool bTooShort = false;
1369 bool bTruncated = false;
1370 bool bHitCoast = false;
1371 bool bHitLand = false;
1372 // bool bHitAnotherProfileBadly = false; // TODO 044
1373
1374 CGeom2DIPoint PtiStart = *pProfile->pPtiGetStartPoint();
1375 CGeom2DIPoint PtiEnd = *pProfile->pPtiGetEndPoint();
1376 CreateRasterizedProfile(nCoast, pProfile, &PtiStart, &PtiEnd, &VCellsToMark, &bVShared, bTooShort, bTruncated, bHitCoast, bHitLand /*, bHitAnotherProfileBadly*/); // TODO 044
1377
1378 if ((bTruncated && (! ACCEPT_SHORT_PROFILES)) || bTooShort || bHitCoast || bHitLand /*|| bHitAnotherProfileBadly*/)
1379 continue;
1380
1381 // This profile is fine
1382 nValidProfiles++;
1383
1384 for (unsigned int k = 0; k < VCellsToMark.size(); k++)
1385 {
1386 // So mark each cell in the raster grid
1387 m_pRasterGrid->m_Cell[VCellsToMark[k].nGetX()][VCellsToMark[k].nGetY()].SetProfileID(nProfile);
1388
1389 // Store the raster grid coordinates in the profile object
1390 m_VCoast[nCoast].pGetProfile(nProfile)->AppendCellInProfile(VCellsToMark[k].nGetX(), VCellsToMark[k].nGetY());
1391
1392 // And also store the external coordinates in the profile object
1393 m_VCoast[nCoast].pGetProfile(nProfile)->AppendCellInProfileExtCRS(dGridCentroidXToExtCRSX(VCellsToMark[k].nGetX()), dGridCentroidYToExtCRSY(VCellsToMark[k].nGetY()));
1394
1395 // Mark the shared (i.e. multi-line) parts of the profile (if any)
1396 // if (bVShared[k])
1397 // {
1398 // m_VCoast[nCoast].pGetProfile(nProfile)->AppendPointShared(true);
1399 // // LogStream << m_ulIter << ": profile " << j << " point " << k << " marked as shared" << endl;
1400 // }
1401 // else
1402 // {
1403 // m_VCoast[nCoast].pGetProfile(nProfile)->AppendPointShared(false);
1404 // // LogStream << m_ulIter << ": profile " << nProfile << " point " << k << " marked as NOT shared" << endl;
1405 // }
1406
1407 }
1408
1409 // Get the deep water wave height and orientation values at the end of the profile
1410 double dDeepWaterWaveHeight = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWaveHeight();
1411 double dDeepWaterWaveAngle = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWaveAngle();
1412 double dDeepWaterWavePeriod = m_pRasterGrid->m_Cell[VCellsToMark.back().nGetX()][VCellsToMark.back().nGetY()].dGetCellDeepWaterWavePeriod();
1413
1414 // And store them for this profile
1415 m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWaveHeight(dDeepWaterWaveHeight);
1416 m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWaveAngle(dDeepWaterWaveAngle);
1417 m_VCoast[nCoast].pGetProfile(nProfile)->SetProfileDeepWaterWavePeriod(dDeepWaterWavePeriod);
1418 }
1419
1420 bDownCoast = ! bDownCoast;
1421}
1422
1423//===============================================================================================================================
1425//===============================================================================================================================
1426void CSimulation::CreateRasterizedProfile(int const nCoast, CGeomProfile* pProfile, CGeom2DIPoint const* pPtiStart, CGeom2DIPoint const* pPtiEnd, vector<CGeom2DIPoint>* pVIPointsOut, vector<bool>* pbVShared, bool& bTooShort, bool& bTruncated, bool& bHitCoast, bool& bHitLand /*, bool& bHitAnotherProfileBadly*/) // TODO 044
1427{
1428 int nProfile = pProfile->nGetCoastID();
1429 int nSeg = 0;
1430 int nNumSegments = pProfile->nGetNumLineSegments();
1431
1432 pVIPointsOut->clear();
1433
1434 // This contains a list of 'other' profiles which intersect this profile, but which are OK (i.e. they are coincident profiles of this profile). Added for efficiency
1435 vector<int> VnOtherProfileOK;
1436
1437 // LogStream << m_ulIter << ": in CreateRasterizedProfile() *pPtiStart for profile " << nProfile << " is [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "]" << endl;
1438 int nXStartLast = INT_NODATA;
1439 int nYStartLast = INT_NODATA;
1440 int nXEndLast = INT_NODATA;
1441 int nYEndLast = INT_NODATA;
1442
1443 // Do for every segment of this profile
1444 for (nSeg = 0; nSeg < nNumSegments; nSeg++)
1445 {
1446 // Do once for every line segment
1447 vector<CGeom2DPoint> PtVSegment;
1448
1449 PtVSegment.push_back(*pProfile->pPtGetPointInProfile(nSeg));
1450 PtVSegment.push_back(*pProfile->pPtGetPointInProfile(nSeg + 1)); // This is OK
1451
1452 bool bShared = false;
1453 if (pProfile->nGetNumCoincidentProfilesInLineSegment(nSeg) > 1)
1454 bShared = true;
1455
1456 // The start point of the normal, must convert from the external CRS to grid CRS. If this is the first line segment of the profile, then the start point is the centroid of a coastline cell
1457 int nXStart = pPtiStart->nGetX();
1458 int nYStart = pPtiStart->nGetY();
1459
1460 // The end point of the normal, again convert from the external CRS to grid CRS. Note too that it could be off the grid
1461 int nXEnd = pPtiEnd->nGetX();
1462 int nYEnd = pPtiEnd->nGetY();
1463
1464 // Safety check
1465 if ((nXStart == nXEnd) && (nYStart == nYEnd))
1466 continue;
1467
1468 // If this is a coincident line segment (i.e. it has the same start and end points as the previous line segment) then ignore it
1469 if ((nXStart == nXStartLast) && (nYStart == nYStartLast) && (nXEnd == nXEndLast) && (nYEnd == nYEndLast))
1470 continue;
1471
1472 // 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
1473 double dXInc = nXEnd - nXStart;
1474 double dYInc = nYEnd - nYStart;
1475 double dLength = tMax(tAbs(dXInc), tAbs(dYInc));
1476
1477 dXInc /= dLength;
1478 dYInc /= dLength;
1479
1480 double dX = nXStart;
1481 double dY = nYStart;
1482
1483 // Process each interpolated point
1484 for (int m = 0; m <= nRound(dLength); m++)
1485 {
1486 int nX = nRound(dX);
1487 int nY = nRound(dY);
1488
1489 // Do some checking of this interpolated point, but only if this is not a grid-edge profile (these profiles are always valid)
1490 if ((! pProfile->bStartOfCoast()) && (! pProfile->bEndOfCoast()))
1491 {
1492 // Is the interpolated point within the valid raster grid?
1493 if (! bIsWithinValidGrid(nX, nY))
1494 {
1495 // It is outside the valid grid, so mark this profile and quit the loop
1496 bTruncated = true;
1497
1499 pProfile->SetTruncated(true);
1500
1502 LogStream << m_ulIter << ": profile " << nProfile << " is invalid, truncated at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1503
1504 break;
1505 }
1506
1507 // If this is the first line segment of the profile, then once we are clear of the coastline (say, when m > 1), check if this profile hits land at this interpolated point. NOTE Get problems here since if the coastline vector has been heavily smoothed, this can result is 'false positives' profiles marked as invalid which are not actually invalid, because the profile hits land when m = 0 or m = 1. This results in some cells being flagged as profile cells which are actually inland
1509 {
1510 if (m_pRasterGrid->m_Cell[nX][nY].bIsCoastline())
1511 {
1512 // We've hit a coastline so set a switch and mark the profile, then quit
1513 bHitCoast = true;
1514 pProfile->SetHitCoast(true);
1515
1517 LogStream << m_ulIter << ": coast " << nCoast << " profile " << nProfile << " is invalid, hit coast at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1518
1519 return;
1520 }
1521
1522 // 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)
1523 if (bIsWithinValidGrid(nX, nY + 1) && m_pRasterGrid->m_Cell[nX][nY + 1].bIsCoastline())
1524 {
1525 // We've hit a coastline so set a switch and mark the profile, then quit
1526 bHitCoast = true;
1527 pProfile->SetHitCoast(true);
1528
1530 LogStream << m_ulIter << ": coast " << nCoast << " profile " << nProfile << " is invalid, hit coast at [" << nX << "][" << nY + 1 << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY + 1) << "}" << endl;
1531
1532 return;
1533 }
1534
1535 if (! m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea()) // TODO check if is intervention?
1536 {
1537 // We've hit dry land (or an intervention) so set a switch and mark the profile
1538 bHitLand = true;
1539 pProfile->SetHitLand(true);
1540
1541 LogStream << m_ulIter << ": profile " << nProfile << " HIT LAND at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation = " << m_pRasterGrid->m_Cell[nX][nY].dGetSedimentTopElev() << ", SWL = " << m_dThisIterSWL << endl;
1542 //
1543 // LogStream << "On [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, sea depth = " << m_pRasterGrid->m_Cell[nX][nY].dGetSeaDepth() << ", bIsInContiguousSea = " << (m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea() ? "true" : "false") << ", landform = " << (m_pRasterGrid->m_Cell[nX][nY].bIsInContiguousSea() ? "sea" : "not sea") << endl;
1544 //
1545 // LogStream << "On [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation of consolidated sediment = " << m_pRasterGrid->m_Cell[nX][nY].dGetConsSedTopForLayerAboveBasement(m_pRasterGrid->m_Cell[nX][nY].nGetTopNonZeroLayerAboveBasement()) << ", total cliff collapse = " << m_pRasterGrid->m_Cell[nX][nY].dGetTotCliffCollapse() << ", total beach deposition = " << m_pRasterGrid->m_Cell[nX][nY].dGetTotBeachDeposition() << endl;
1546
1547 return;
1548 }
1549 }
1550
1551 // Now check to see if we hit another profile which is not a coincident normal to this normal
1552 if (m_pRasterGrid->m_Cell[nX][nY].bIsProfile())
1553 {
1554 // We've hit a raster cell which is already marked as 'under' a normal profile. Get the number of the profile which marked this cell
1555 int nHitProfile = m_pRasterGrid->m_Cell[nX][nY].nGetProfileID();
1556
1557 // Search for the 'other' profile in the list of OK profiles
1558 if (find(VnOtherProfileOK.begin(), VnOtherProfileOK.end(), nHitProfile) == VnOtherProfileOK.end())
1559 {
1560 // Not found, so it must be the first time we've encountered this 'other' profile
1561 // LogStream << "VVVV Profile " << nProfile << " touches profile " << nHitProfile << endl;
1562 VnOtherProfileOK.push_back(nHitProfile);
1563
1564 // TODO 044 Will need this if hit a profile which belongs to a different coast
1565 // if (nHitProfile > nNumProfiles - 1)
1566 // {
1567 // bHitAnotherProfileBadly = true;
1568 // pProfile->SetHitAnotherProfileBadly(true);
1569 //
1570 // if (m_nLogFileDetail >= LOG_FILE_ALL)
1571 // LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, crosses another profile A1 (" << nHitProfile << ") at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1572 //
1573 // return;
1574 // }
1575
1576 // Only set the flag if this isn't a coincident normal to one or other of the profiles
1577// // DEBUG CODE ==============================================================================================
1578// bool
1579// bOtherConcidentToThis = pProfile->bFindProfileInCoincidentProfiles(nHitProfile),
1580// bThisCoincidentToOther = m_VCoast[nCoast].pGetProfile(nHitProfile)->bFindProfileInCoincidentProfiles(nProfile);
1581//
1582// if (bOtherConcidentToThis)
1583// LogStream << "Profile " << nHitProfile << " is coincident with " << nProfile << endl;
1584//
1585// if (bThisCoincidentToOther)
1586// LogStream << "Profile " << nProfile << " is coincident with " << nHitProfile << endl;
1587//
1588// if ((! bOtherConcidentToThis) && (! bThisCoincidentToOther))
1589// // DEBUG CODE ==============================================================================================
1590
1591 // Note only need to test whether the 'other' profile is concident with his since the coincidence relationship is mutual
1592 // if ((! pProfile->bFindProfileInCoincidentProfiles(nHitProfile)) && (! m_VCoast[nCoast].pGetProfile(nHitProfile)->bFindProfileInCoincidentProfiles(nProfile)))
1593 // if (! pProfile->bFindProfileInCoincidentProfiles(nHitProfile))
1594 // {
1595 // bHitAnotherProfileBadly = true;
1596 // pProfile->SetHitAnotherProfileBadly(true);
1597 //
1598 // if (m_nLogFileDetail >= LOG_FILE_ALL)
1599 // LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, crosses another profile B1 (" << nHitProfile << ") at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1600 //
1601 // return;
1602 // }
1603 }
1604 }
1605
1606 // Try diagonal too
1607 int nXTmp = nX + 1;
1608 int nYTmp = nY;
1609
1610 if (bIsWithinValidGrid(nXTmp, nYTmp) && m_pRasterGrid->m_Cell[nXTmp][nYTmp].bIsProfile())
1611 {
1612 // We've hit a raster cell which is already marked as 'under' a normal profile. Get the number of the profile which marked this cell
1613 int nHitProfile = m_pRasterGrid->m_Cell[nXTmp][nYTmp].nGetProfileID();
1614
1615 // Search for the 'other' profile in the list of OK profiles
1616 if (find(VnOtherProfileOK.begin(), VnOtherProfileOK.end(), nHitProfile) == VnOtherProfileOK.end())
1617 {
1618 // Not found, so it must be the first time we've encountered this 'other' profile
1619 // LogStream << "VVVV Profile " << nProfile << " touches profile " << nHitProfile << " diagonally" << endl;
1620 VnOtherProfileOK.push_back(nHitProfile);
1621
1622 // TODO 044 Will need this if we hit a profile which belongs to a different coast
1623 // if (nHitProfile > nNumProfiles - 1)
1624 // {
1625 // bHitAnotherProfileBadly = true;
1626 // pProfile->SetHitAnotherProfileBadly(true);
1627 //
1628 // if (m_nLogFileDetail >= LOG_FILE_ALL)
1629 // LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, crosses another profile diagonally A2 (" << nHitProfile << ") at [" << nXTmp << "][" << nYTmp << "] = {" << dGridCentroidXToExtCRSX(nXTmp) << ", " << dGridCentroidYToExtCRSY(nYTmp) << "}" << endl;
1630 //
1631 // return;
1632 // }
1633
1634 // Only set the flag if this isn't a coincident normal to one or other of the profiles
1635// // DEBUG CODE ==============================================================================================
1636// bool
1637// bOtherConcidentToThis = pProfile->bFindProfileInCoincidentProfiles(nHitProfile),
1638// bThisCoincidentToOther = m_VCoast[nCoast].pGetProfile(nHitProfile)->bFindProfileInCoincidentProfiles(nProfile);
1639//
1640// if (bOtherConcidentToThis)
1641// LogStream << "Profile " << nHitProfile << " is coincident with " << nProfile << endl;
1642//
1643// if (bThisCoincidentToOther)
1644// LogStream << "Profile " << nProfile << " is coincident with " << nHitProfile << endl;
1645//
1646// if ((! bOtherConcidentToThis) && (! bThisCoincidentToOther))
1647// // DEBUG CODE ==============================================================================================
1648
1649 // Note only need to test whether the 'other' profile is concident with his since the concidence relationship is mutual
1650 // if ((! pProfile->bFindProfileInCoincidentProfiles(nHitProfile)) && (! m_VCoast[nCoast].pGetProfile(nHitProfile)->bFindProfileInCoincidentProfiles(nProfile)))
1651 // if (! pProfile->bFindProfileInCoincidentProfiles(nHitProfile))
1652 // {
1653 // bHitAnotherProfileBadly = true;
1654 // pProfile->SetHitAnotherProfileBadly(true);
1655 //
1656 // if (m_nLogFileDetail >= LOG_FILE_ALL)
1657 // LogStream << m_ulIter << ": coast " << nCoast << ", profile " << nProfile << " is invalid, crosses another profile diagonally B2 (" << nHitProfile << ") at [" << nXTmp << "][" << nYTmp << "] = {" << dGridCentroidXToExtCRSX(nXTmp) << ", " << dGridCentroidYToExtCRSY(nYTmp) << "}" << endl;
1658 //
1659 // return;
1660 // }
1661 }
1662 }
1663 }
1664
1665 // Append this point to the output vector
1666 pVIPointsOut->push_back(CGeom2DIPoint(nX, nY)); // Is in raster grid coordinates
1667 pbVShared->push_back(bShared);
1668
1669 // And increment for next time
1670 dX += dXInc;
1671 dY += dYInc;
1672 }
1673
1674 nXStartLast = nXStart;
1675 nYStartLast = nYStart;
1676 nXEndLast = nXEnd;
1677 nYEndLast = nYEnd;
1678
1679 if (bTruncated)
1680 break;
1681 }
1682
1683 if (bTruncated)
1684 {
1685 if (nSeg < (nNumSegments - 1))
1686 // We are truncating the profile, so remove any line segments after this one
1687 pProfile->TruncateLineSegments(nSeg);
1688
1689 // Shorten the vector input. Ignore CPPCheck errors here, since we know that pVIPointsOut is not empty
1690 int nLastX = pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX();
1691 int nLastY = pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY();
1692
1693 pProfile->pPtGetPointInProfile(nSeg + 1)->SetX(dGridCentroidXToExtCRSX(nLastX));
1694 pProfile->pPtGetPointInProfile(nSeg + 1)->SetY(dGridCentroidYToExtCRSY(nLastY));
1695 }
1696
1697 // // DEBUG CODE =====================================================================================
1698 // LogStream << "====================" << endl;
1699 // LogStream << m_ulIter << ": for profile " << nProfile << " pPtiStart = [" << pPtiStart->nGetX() << "][" << pPtiStart->nGetY() << "] pPtiEnd = [" << pPtiEnd->nGetX() << "][" << pPtiEnd->nGetY() << "] pVIPointsOut->size() = " << pVIPointsOut->size() << endl;
1700 // // for (int n = 0; n < static_cast<int>(pVIPointsOut->size()); n++)
1701 // // LogStream << "\t[" << pVIPointsOut->at(n).nGetX() << "][" << pVIPointsOut->at(n).nGetY() << "]" << endl;
1702 // LogStream << "====================" << endl;
1703 // // DEBUG CODE =====================================================================================
1704
1705 if (pVIPointsOut->size() < 3)
1706 {
1707 // Coastline-normal profiles cannot be very short (e.g. with less than 3 cells), since we cannot calculate along-profile slope properly for such short profiles
1708 bTooShort = true;
1709 pProfile->SetTooShort(true);
1710
1712 {
1713 // Ignore CPPCheck errors here, since we know that pVIPointsOut is not empty
1714 LogStream << m_ulIter << ": profile " << nProfile << " is invalid, is too short, only " << pVIPointsOut->size() << " points, HitLand?" << bHitLand << ". From [" << pVIPointsOut->at(0).nGetX() << "][" << pVIPointsOut->at(0).nGetY() << "] = {" << dGridCentroidXToExtCRSX(pVIPointsOut->at(0).nGetX()) << ", " << dGridCentroidYToExtCRSY(pVIPointsOut->at(0).nGetY()) << "} to [" << pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX() << "][" << pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY() << "] = {" << dGridCentroidXToExtCRSX(pVIPointsOut->at(pVIPointsOut->size() - 1).nGetX()) << ", " << dGridCentroidYToExtCRSY(pVIPointsOut->at(pVIPointsOut->size() - 1).nGetY()) << "}" << endl;
1715 }
1716 }
1717}
1718
1719//===============================================================================================================================
1721//===============================================================================================================================
1722void CSimulation::MergeProfilesAtFinalLineSegments(int const nCoast, CGeomProfile* pFirstProfile, CGeomProfile* pSecondProfile, int const nFirstProfileLineSegments, int const nSecondProfileLineSegments, double const dIntersectX, double const dIntersectY, double const dAvgEndX, double const dAvgEndY)
1723{
1724 // The point of intersection is on the final (most seaward) line segment of both profiles. Put together a vector of coincident profile numbers (with no duplicates) for both profiles
1725 int nCombinedLastSeg = 0;
1726 vector<pair<int, int>> prVCombinedProfilesCoincidentProfilesLastSeg;
1727
1728 for (unsigned int n = 0; n < pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->size(); n++)
1729 {
1730 pair<int, int> prTmp;
1731 prTmp.first = pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->at(n).first;
1732 prTmp.second = pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1)->at(n).second;
1733
1734 bool bFound = false;
1735 for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++)
1736 {
1737 if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first)
1738 {
1739 bFound = true;
1740 break;
1741 }
1742 }
1743
1744 if (! bFound)
1745 {
1746 prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp);
1747 nCombinedLastSeg++;
1748 }
1749 }
1750
1751 for (unsigned int n = 0; n < pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->size(); n++)
1752 {
1753 pair<int, int> prTmp;
1754 prTmp.first = pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->at(n).first;
1755 prTmp.second = pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1)->at(n).second;
1756
1757 bool bFound = false;
1758 for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++)
1759 {
1760 if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first)
1761 {
1762 bFound = true;
1763 break;
1764 }
1765 }
1766
1767 if (! bFound)
1768 {
1769 prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp);
1770 nCombinedLastSeg++;
1771 }
1772 }
1773
1774 // Increment the number of each line segment
1775 for (int m = 0; m < nCombinedLastSeg; m++)
1776 prVCombinedProfilesCoincidentProfilesLastSeg[m].second++;
1777
1778 vector<pair<int, int>> prVFirstProfileCoincidentProfilesLastSeg = *pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(nFirstProfileLineSegments - 1);
1779 vector<pair<int, int>> prVSecondProfileCoincidentProfilesLastSeg = *pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(nSecondProfileLineSegments - 1);
1780 int nNumFirstProfileCoincidentProfilesLastSeg = static_cast<int>(prVFirstProfileCoincidentProfilesLastSeg.size());
1781 int nNumSecondProfileCoincidentProfilesLastSeg = static_cast<int>(prVSecondProfileCoincidentProfilesLastSeg.size());
1782
1783 // LogStream << m_ulIter << ": END-SEGMENT INTERSECTION between profiles " << nFirstProfile << " and " << nSecondProfile << " at line segment " << nFirstProfileLineSegments-1 << "/" << nFirstProfileLineSegments-1 << ", and line segment " << nSecondProfileLineSegments-1 << "/" << nSecondProfileLineSegments-1 << ", respectively. Both truncated at [" << dIntersectX << ", " << dIntersectY << "] then profiles {" << nFirstProfile << "} and {" << nSecondProfile << "} extended to [" << dAvgEndX << ", " << dAvgEndY << "]" << endl;
1784
1785 // Truncate the first profile, and all co-incident profiles, at the point of intersection
1786 for (int n = 0; n < nNumFirstProfileCoincidentProfilesLastSeg; n++)
1787 {
1788 int nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[n].first;
1789 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1790 int nProfileLength = pThisProfile->nGetProfileSize();
1791
1792 // This is the final line segment of the first 'main' profile. We are assuming that it is also the final line segment of all co-incident profiles. This is fine, altho' each profile may well have a different number of line segments landwards i.e. the number of the line segment may be different for each co-incident profile
1793 pThisProfile->SetPointInProfile(nProfileLength - 1, dIntersectX, dIntersectY);
1794 }
1795
1796 // Truncate the second profile, and all co-incident profiles, at the point of intersection
1797 for (int n = 0; n < nNumSecondProfileCoincidentProfilesLastSeg; n++)
1798 {
1799 int nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[n].first;
1800 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1801 int nProfileLength = pThisProfile->nGetProfileSize();
1802
1803 // This is the final line segment of the second 'main' profile. We are assuming that it is also the final line segment of all co-incident profiles. This is fine, altho' each profile may well have a different number of line segments landwards i.e. the number of the line segment may be different for each co-incident profile
1804 pThisProfile->SetPointInProfile(nProfileLength - 1, dIntersectX, dIntersectY);
1805 }
1806
1807 // Append a new straight line segment to the existing line segment(s) of the first profile, and to all co-incident profiles
1808 for (int nThisLineSeg = 0; nThisLineSeg < nNumFirstProfileCoincidentProfilesLastSeg; nThisLineSeg++)
1809 {
1810 int nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[nThisLineSeg].first;
1811 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1812
1813 // Update this profile
1814 pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY);
1815
1816 // Append details of the combined profiles
1817 pThisProfile->AppendLineSegment();
1818 for (int m = 0; m < nCombinedLastSeg; m++)
1819 pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]);
1820 }
1821
1822 // Append a new straight line segment to the existing line segment(s) of the second profile, and to all co-incident profiles
1823 for (int nThisLineSeg = 0; nThisLineSeg < nNumSecondProfileCoincidentProfilesLastSeg; nThisLineSeg++)
1824 {
1825 int nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[nThisLineSeg].first;
1826 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1827
1828 // Update this profile
1829 pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY);
1830
1831 // Append details of the combined profiles
1832 pThisProfile->AppendLineSegment();
1833 for (int m = 0; m < nCombinedLastSeg; m++)
1834 pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]);
1835 }
1836
1837 // // DEBUG CODE ****************************************************************
1838 // int nFirstProfileLineSeg= pFirstProfile->nGetNumLineSegments();
1839 // int nSecondProfileLineSeg = pSecondProfile->nGetNumLineSegments();
1840 //
1841 // LogStream << "\tProfile {" << nFirstProfile << "} now has " << nFirstProfileLineSeg << " line segments" << endl;
1842 // for (int m = 0; m < nFirstProfileLineSeg; m++)
1843 // {
1844 // vector<pair<int, int> > prVCoincidentProfiles = *pFirstProfile->pprVGetPairedCoincidentProfilesForLineSegment(m);
1845 // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nFirstProfile << "} are {";
1846 // for (int nn = 0; nn < prVCoincidentProfiles.size(); nn++)
1847 // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] ";
1848 // LogStream << " }" << endl;
1849 // }
1850 // LogStream << "\tProfile {" << nSecondProfile << "} now has " << nSecondProfileLineSeg << " line segments" << endl;
1851 // for (int m = 0; m < nSecondProfileLineSeg; m++)
1852 // {
1853 // vector<pair<int, int> > prVCoincidentProfiles = *pSecondProfile->pprVGetPairedCoincidentProfilesForLineSegment(m);
1854 // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nSecondProfile << "} are {";
1855 // for (int nn = 0; nn < prVCoincidentProfiles.size(); nn++)
1856 // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] ";
1857 // LogStream << " }" << endl;
1858 // }
1859 // // DEBUG CODE ******************************************************************
1860}
1861
1862//===============================================================================================================================
1864//===============================================================================================================================
1865void CSimulation::TruncateOneProfileRetainOtherProfile(int const nCoast, CGeomProfile* pProfileToTruncate, CGeomProfile* pProfileToRetain, double const dIntersectX, double const dIntersectY, int const nProfileToTruncateIntersectLineSeg, int const nProfileToRetainIntersectLineSeg, bool const bAlreadyPresent)
1866{
1867 // Insert the intersection point into the main retain-profile if it is not already in the profile, and do the same for all co-incident profiles of the main retain-profile. Also add details of the to-truncate profile (and all its coincident profiles) to every line segment of the main to-retain profile which is seaward of the point of intersection
1868 int nRet = nInsertPointIntoProfilesIfNeededThenUpdate(nCoast, pProfileToRetain, dIntersectX, dIntersectY, nProfileToRetainIntersectLineSeg, pProfileToTruncate, nProfileToTruncateIntersectLineSeg, bAlreadyPresent);
1869 if (nRet != RTN_OK)
1870 {
1871 LogStream << m_ulIter << ": error in nInsertPointIntoProfilesIfNeededThenUpdate()" << endl;
1872 return;
1873 }
1874
1875 // Get all profile points of the main retain-profile seawards from the intersection point, and do the same for the corresponding line segments (including coincident profiles). This also includes details of the main to-truncate profile (and all its coincident profiles)
1876 vector<CGeom2DPoint> PtVProfileLastPart;
1877 vector<vector<pair<int, int>>> prVLineSegLastPart;
1878 if (bAlreadyPresent)
1879 {
1880 PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg);
1881 prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg);
1882 }
1883 else
1884 {
1885 PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg + 1);
1886 prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg + 1);
1887 }
1888
1889 // assert(PtVProfileLastPart.size() > 1);
1890 // assert(prVLineSegLastPart.size() > 0);
1891
1892 // Truncate the truncate-profile at the point of intersection, and do the same for all its co-incident profiles. Then append the profile points of the main to-retain profile seaward from the intersection point, and do the same for the corresponding line segments (including coincident profiles)
1893 TruncateProfileAndAppendNew(nCoast, pProfileToTruncate, nProfileToTruncateIntersectLineSeg, &PtVProfileLastPart, &prVLineSegLastPart);
1894
1895 // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetProfileSize() > 1);
1896 // assert(pProfileToRetain->nGetNumLineSegments() > 0);
1897 // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetNumLineSegments() > 0);
1898}
1899
1900//===============================================================================================================================
1902//===============================================================================================================================
1903int CSimulation::nInsertPointIntoProfilesIfNeededThenUpdate(int const nCoast, CGeomProfile* pProfileToRetain, double const dIntersectX, double const dIntersectY, int const nProfileToRetainIntersectLineSeg, CGeomProfile* pProfileToTruncate, int const nProfileToTruncateIntersectLineSeg, bool const bAlreadyPresent)
1904{
1905 // // DEBUG CODE ****************************************************************
1906 // // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurred
1907 // vector<pair<int, int> > prVRetainCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg);
1908 // int nNumRetainCoincidentCHECK1 = prVRetainCoincidentProfilesCHECK1.size();
1909 // for (int nn = 0; nn < nNumRetainCoincidentCHECK1; nn++)
1910 // {
1911 // int nThisProfile = prVRetainCoincidentProfilesCHECK1[nn].first;
1912 // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ";
1913 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1914 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1915 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are ";
1916 // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++)
1917 // {
1918 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm);
1919 // LogStream << "{ ";
1920 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++)
1921 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1922 // LogStream << "} ";
1923 // }
1924 // LogStream << endl;
1925 //
1926 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1927 // {
1928 // CGeom2DPoint
1929 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1930 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1931 //
1932 // if (Pt1 == Pt2)
1933 // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1934 // }
1935 // }
1936 //
1937 // // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred
1938 // vector<pair<int, int> > prVTruncateCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg);
1939 // int nNumTruncateCoincidentCHECK1 = prVTruncateCoincidentProfilesCHECK1.size();
1940 // for (int nn = 0; nn < nNumTruncateCoincidentCHECK1; nn++)
1941 // {
1942 // int nThisProfile = prVTruncateCoincidentProfilesCHECK1[nn].first;
1943 // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToTruncate ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ";
1944 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1945 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1946 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are ";
1947 // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++)
1948 // {
1949 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm);
1950 // LogStream << "{ ";
1951 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++)
1952 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1953 // LogStream << "} ";
1954 // }
1955 // LogStream << endl;
1956 //
1957 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1958 // {
1959 // CGeom2DPoint
1960 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1961 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1962 //
1963 // if (Pt1 == Pt2)
1964 // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1965 // }
1966 // }
1967 // // DEBUG CODE ******************************************************************
1968
1969 int nProfileToRetain = pProfileToRetain->nGetCoastID();
1970
1971 // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurs
1972 vector<pair<int, int>> prVCoincidentProfiles = *pProfileToRetain->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg);
1973 int nNumCoincident = static_cast<int>(prVCoincidentProfiles.size());
1974 vector<int> nLineSegAfterIntersect(nNumCoincident, -1); // The line segment after the point of intersection, for each co-incident profile
1975
1976 // Do this for the main profile and all profiles which are co-incident for this line segment
1977 for (int nn = 0; nn < nNumCoincident; nn++)
1978 {
1979 int nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile
1980 int nThisLineSeg = prVCoincidentProfiles[nn].second; // The line segment of this profile
1981 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1982
1983 // Is the intersection point already present in the to-retain profile?
1984 if (! bAlreadyPresent)
1985 {
1986 // It is not already present, so insert it and also update the associated multi-line
1987 if (! pThisProfile->bInsertIntersection(dIntersectX, dIntersectY, nThisLineSeg))
1988 {
1989 // Error
1990 LogStream << WARN << m_ulIter << ": cannot insert a line segment after the final line segment (" << nThisLineSeg << ") for " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " profile (" << nThisProfile << "), abandoning" << endl;
1991
1993 }
1994
1995 // LogStream << "\tIntersection point NOT already in " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " profile {" << nThisProfile << "}, inserted it as point " << nThisLineSeg+1 << endl;
1996 }
1997
1998 // Get the line segment after intersection
1999 nLineSegAfterIntersect[nn] = nThisLineSeg + 1;
2000 }
2001
2002 // for (int nn = 0; nn < nNumCoincident; nn++)
2003 // LogStream << "\tFor profile " << prVCoincidentProfiles[nn].first << " line segment [" << nLineSegAfterIntersect[nn] << "] is immediately after the intersection point" << endl;
2004
2005 // Get the coincident profiles for the to-truncate profile, at the line segment where intersection occurs
2006 vector<pair<int, int>> prVToTruncateCoincidentProfiles = *pProfileToTruncate->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg);
2007 int nNumToTruncateCoincident = static_cast<int>(prVToTruncateCoincidentProfiles.size());
2008
2009 // Now add the number of the to-truncate profile, and all its coincident profiles, to all line segments which are seaward of the point of intersection. Do this for the main profile and all profiles which are co-incident for this line segment
2010 for (int nn = 0; nn < nNumCoincident; nn++)
2011 {
2012 int nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile
2013 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
2014
2015 // Get the number of line segments for this to-retain profile (will have just increased, if we just inserted a point)
2016 int nNumLineSegs = pThisProfile->nGetNumLineSegments();
2017
2018 // Do for all line segments seaward of the point of intersection
2019 for (int nLineSeg = nLineSegAfterIntersect[nn], nIncr = 0; nLineSeg < nNumLineSegs; nLineSeg++, nIncr++)
2020 {
2021 // This can happen occasionally
2022 // if (nThisProfile == nProfileToTruncateIntersectLineSeg)
2023 // {
2024 // LogStream << "\t*** ERROR nThisProfile = " << nThisProfile << " nProfileToTruncateIntersectLineSeg = " << nProfileToTruncateIntersectLineSeg << ", ignoring" << endl;
2025 // pThisProfile->SetHitAnotherProfileBadly(true);
2026 // continue;
2027 // }
2028
2029 // Add the number of the to-truncate profile, and all its coincident profiles, to this line segment
2030 for (int m = 0; m < nNumToTruncateCoincident; m++)
2031 {
2032 int nProfileToAdd = prVToTruncateCoincidentProfiles[m].first;
2033 int nProfileToAddLineSeg = prVToTruncateCoincidentProfiles[m].second;
2034
2035 // LogStream << "\tAdding " << (nProfileToAdd == nProfileToTruncateIntersectLineSeg ? "main" : "co-incident") << " truncate-profile " << nProfileToAdd << ", line segment [" << nProfileToAddLineSeg + nIncr << "] to line segment " << nLineSeg << " of " << (nThisProfile == nProfileToRetain ? "main" : "co-incident") << " to-retain profile " << nThisProfile << endl;
2036
2037 pThisProfile->AddCoincidentProfileToExistingLineSegment(nLineSeg, nProfileToAdd, nProfileToAddLineSeg + nIncr);
2038 }
2039 }
2040 }
2041
2042 // // DEBUG CODE ****************************************************************
2043 // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred
2044 // vector<pair<int, int> > prVCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nProfileToRetain)->pprVGetPairedCoincidentProfilesForLineSegment(nProfileToRetainIntersectLineSeg);
2045 // int nNumCoincidentCHECK2 = prVCoincidentProfilesCHECK2.size();
2046 // for (int nn = 0; nn < nNumCoincidentCHECK2; nn++)
2047 // {
2048 // int nThisProfile = prVCoincidentProfilesCHECK2[nn].first;
2049 // LogStream << "\tAFTER nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToRetain ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ";
2050 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
2051 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
2052 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are ";
2053 // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++)
2054 // {
2055 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nLineSeg);
2056 // LogStream << "{ ";
2057 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++)
2058 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
2059 // LogStream << "} ";
2060 // }
2061 // LogStream << endl;
2062 //
2063 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
2064 // {
2065 // CGeom2DPoint
2066 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
2067 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
2068 //
2069 // if (Pt1 == Pt2)
2070 // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
2071 // }
2072 // }
2073 // // DEBUG CODE ******************************************************************
2074
2075 return RTN_OK;
2076}
2077
2078//===============================================================================================================================
2080//===============================================================================================================================
2081void CSimulation::TruncateProfileAndAppendNew(int const nCoast, CGeomProfile* pProfileToRetain, int const nMainProfileIntersectLineSeg, vector<CGeom2DPoint> const* pPtVProfileLastPart, vector<vector<pair<int, int>>> const* pprVLineSegLastPart)
2082{
2083 // // DEBUG CODE ****************************************************************
2084 // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred
2085 // vector<pair<int, int> > prVCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
2086 // int nNumCoincidentCHECK1 = prVCoincidentProfilesCHECK1.size();
2087 //
2088 // LogStream << "\tTruncating profile {" << nMainProfile << "}, intersection is at [" << dIntersectX << ", " << dIntersectY << "] in line segment " << nMainProfileIntersectLineSeg << endl;
2089 // for (int nn = 0; nn < nNumCoincidentCHECK1; nn++)
2090 // {
2091 // int nThisProfile = prVCoincidentProfilesCHECK1[nn].first;
2092 // LogStream << "\tBEFORE TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points (";
2093 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
2094 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
2095 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are ";
2096 // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++)
2097 // {
2098 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nLineSeg);
2099 // LogStream << "{ ";
2100 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++)
2101 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
2102 // LogStream << "} ";
2103 // }
2104 // LogStream << endl;
2105 //
2106 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
2107 // {
2108 // CGeom2DPoint
2109 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
2110 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
2111 //
2112 // if (Pt1 == Pt2)
2113 // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
2114 // }
2115 // }
2116 // LogStream << "\tPart-profile to append is ";
2117 // for (int mm = 0; mm < pPtVProfileLastPart->size(); mm++)
2118 // LogStream << "[" << pPtVProfileLastPart->at(mm).dGetX() << ", " << pPtVProfileLastPart->at(mm).dGetY() << "] ";
2119 // LogStream << endl;
2120 // LogStream << "\tPart line-segment to append is ";
2121 // for (int mm = 0; mm < pprVLineSegLastPart->size(); mm++)
2122 // {
2123 // vector<pair<int, int> > prVTmp = pprVLineSegLastPart->at(mm);
2124 // LogStream << "{ ";
2125 // for (int nn = 0; nn < prVTmp.size(); nn++)
2126 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
2127 // LogStream << "} ";
2128 // }
2129 // LogStream << endl;
2130 // // DEBUG CODE ******************************************************************
2131
2132 // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurs
2133 vector<pair<int, int>> prVCoincidentProfiles = *pProfileToRetain->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
2134 int nNumCoincident = static_cast<int>(prVCoincidentProfiles.size());
2135
2136 for (int nn = 0; nn < nNumCoincident; nn++)
2137 {
2138 // Do this for the main to-truncate profile, and do the same for all its co-incident profiles
2139 int nThisProfile = prVCoincidentProfiles[nn].first;
2140 int nThisProfileLineSeg = prVCoincidentProfiles[nn].second;
2141 CGeomProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
2142
2143 // if (nThisProfile == nMainProfile)
2144 // assert(nThisProfileLineSeg == nMainProfileIntersectLineSeg);
2145
2146 // Truncate the profile
2147 // LogStream << "\tTruncating " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} at line segment " << nThisProfileLineSeg+1 << endl;
2148 pThisProfile->TruncateProfile(nThisProfileLineSeg + 1);
2149
2150 // Reduce the number of line segments for this profile
2151 pThisProfile->TruncateLineSegments(nThisProfileLineSeg + 1);
2152
2153 // Append the profile points from the last part of the retain-profile
2154 for (unsigned int mm = 0; mm < pPtVProfileLastPart->size(); mm++)
2155 {
2156 CGeom2DPoint Pt = pPtVProfileLastPart->at(mm);
2157 pThisProfile->AppendPointInProfile(&Pt);
2158 }
2159
2160 // Append the line segments, and their co-incident profile numbers, from the last part of the retain-profile
2161 for (unsigned int mm = 0; mm < pprVLineSegLastPart->size(); mm++)
2162 {
2163 vector<pair<int, int>> prVTmp = pprVLineSegLastPart->at(mm);
2164
2165 pThisProfile->AppendLineSegment(&prVTmp);
2166 }
2167
2168 // Fix the line seg numbers for this profile
2169 vector<int> nVProf;
2170 vector<int> nVProfsLineSeg;
2171 for (int nSeg = 0; nSeg < pThisProfile->nGetNumLineSegments(); nSeg++)
2172 {
2173 for (int nCoinc = 0; nCoinc < pThisProfile->nGetNumCoincidentProfilesInLineSegment(nSeg); nCoinc++)
2174 {
2175 int nProf = pThisProfile->nGetProf(nSeg, nCoinc);
2176 int nProfsLineSeg = pThisProfile->nGetProfsLineSeg(nSeg, nCoinc);
2177
2178 auto it = find(nVProf.begin(), nVProf.end(), nProf);
2179 if (it == nVProf.end())
2180 {
2181 // Not found
2182 nVProf.push_back(nProf);
2183 nVProfsLineSeg.push_back(nProfsLineSeg);
2184 }
2185 else
2186 {
2187 // Found
2188 int nPos = static_cast<int>(it - nVProf.begin());
2189 int nNewProfsLineSeg = nVProfsLineSeg[nPos];
2190 nNewProfsLineSeg++;
2191
2192 nVProfsLineSeg[nPos] = nNewProfsLineSeg;
2193 pThisProfile->SetProfsLineSeg(nSeg, nCoinc, nNewProfsLineSeg);
2194 }
2195 }
2196 }
2197
2198 // assert(pThisProfile->nGetProfileSize() > 1);
2199 }
2200
2201 // // DEBUG CODE ****************************************************************
2202 // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred
2203 // vector<pair<int, int> > prVToTruncateCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetPairedCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
2204 // int nNumToTruncateCoincidentCHECK2 = prVToTruncateCoincidentProfilesCHECK2.size();
2205 // for (int nn = 0; nn < nNumToTruncateCoincidentCHECK2; nn++)
2206 // {
2207 // int nThisProfile = prVToTruncateCoincidentProfilesCHECK2[nn].first;
2208 // LogStream << "\tAFTER TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points (";
2209 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
2210 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
2211 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are ";
2212 // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++)
2213 // {
2214 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetPairedCoincidentProfilesForLineSegment(mm);
2215 // LogStream << "{ ";
2216 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++)
2217 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
2218 // LogStream << "} ";
2219 // }
2220 // LogStream << endl;
2221 //
2222 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
2223 // {
2224 // CGeom2DPoint
2225 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
2226 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
2227 //
2228 // if (Pt1 == Pt2)
2229 // LogStream << m_ulIter << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
2230 // }
2231 // }
2232 // // DEBUG CODE ******************************************************************
2233}
vector< CGeom2DPoint > * pPtVGetPoints(void)
Returns the address of the vector which represents this 2D shape.
Definition 2d_shape.cpp:130
Geometry class used to represent 2D point objects with integer coordinates.
Definition 2di_point.h:29
int nGetY(void) const
Returns the CGeom2DIPoint object's integer Y coordinate.
Definition 2di_point.cpp:48
void SetXY(int const, int const)
The two integer parameters set values for the CGeom2DIPoint object's X and Y coordinates.
Definition 2di_point.cpp:78
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
void SetY(double const)
The double parameter sets a value for the CGeom2DIPoint object's Y coordinate.
Definition 2d_point.cpp:58
double dGetY(void) const
Returns the CGeom2DPoint object's double Y coordinate.
Definition 2d_point.cpp:46
double dGetX(void) const
Returns the CGeom2DPoint object's double X coordinate.
Definition 2d_point.cpp:40
void SetX(double const)
The double parameter sets a value for the CGeom2DIPoint object's X coordinate.
Definition 2d_point.cpp:52
int nGetProfsLineSeg(int const, int const) const
Returns the profile's own line segment, given a line segment and the index of the co-incident profile...
bool bFindProfileInCoincidentProfilesOfLastLineSegment(int const)
Returns true if the given profile number is amongst the coincident profiles of the CGeomMultiLine obj...
int nGetNumCoincidentProfilesInLineSegment(int const)
Returns the count of coincident profiles in a specified line segment.
void AppendLineSegment(void)
Appends a new empty line segment.
int nGetProf(int const, int const) const
Returns the profile number, given a line segment and the index of the co-incident profile for that li...
void TruncateLineSegments(int const)
Cuts short the number of line segments.
vector< vector< pair< int, int > > > prVVGetAllLineSegAfter(int const)
Returns a vector of the line segments which succeed the specified line segment number.
void AppendCoincidentProfileToLineSegments(pair< int, int > const)
Appends a coincident profile pair to the CGeomMultiLine object's final line segment.
void SetProfsLineSeg(int const, int const, int const)
Sets a profile's own line segment number, given a line segment and the index of the co-incident profi...
vector< pair< int, int > > * pprVGetPairedCoincidentProfilesForLineSegment(int const)
Returns a vector of pairs (a line segment)
void AddCoincidentProfileToExistingLineSegment(int const, int const, int const)
Adds a coincident profile to a pre-existing line segment of the CGeomMultiLine object.
int nGetNumLineSegments(void) const
Appends a line segment which then inherits from the preceding line segments.
Geometry class used to represent coast profile objects.
Definition profile.h:35
void TruncateProfile(int const)
Truncates the profile.
Definition profile.cpp:305
void SetEndOfCoast(bool const)
Sets a switch to indicate whether this is an end-of-coast profile.
Definition profile.cpp:114
void AppendPointInProfile(double const, double const)
Appends a point to the profile.
Definition profile.cpp:274
void SetProfileDeepWaterWavePeriod(double const)
Sets the deep-water wave Period for this profile.
Definition profile.cpp:571
bool bIsPointInProfile(double const, double const)
Removes a line segment from the profile.
Definition profile.cpp:351
void SetHitCoast(bool const)
Sets a switch which indicates whether this profile has hit a coast.
Definition profile.cpp:150
bool bProfileOKIncTruncated(void) const
Returns true if this is a problem-free profile, and is not a start-of-coast or an end-of-coast profil...
Definition profile.cpp:215
void SetStartOfCoast(bool const)
Sets a switch to indicate whether this is a start-of-coast profile.
Definition profile.cpp:102
void AppendCellInProfileExtCRS(double const, double const)
Appends a cell (specified in the external coordinate system) to the profile.
Definition profile.cpp:502
void SetUpCoastAdjacentProfile(CGeomProfile *)
Definition profile.cpp:434
vector< CGeom2DPoint > PtVGetThisPointAndAllAfter(int const)
Returns a given point from the profile, and all points after this.
Definition profile.cpp:338
void SetTruncated(bool const)
Sets a switch which indicates whether this profile is truncated.
Definition profile.cpp:174
bool bInsertIntersection(double const, double const, int const)
Inserts an intersection into the profile.
Definition profile.cpp:286
bool bProfileOK(void) const
Returns true if this is a problem-free profile, and is not a start-of-coast or an end-of-coast profil...
Definition profile.cpp:198
void SetProfileDeepWaterWaveHeight(double const)
Sets the deep-water wave height for this profile.
Definition profile.cpp:547
void SetDownCoastAdjacentProfile(CGeomProfile *)
Definition profile.cpp:444
bool bIsIntervention(void) const
Returns true if this is an intervention profile.
Definition profile.cpp:583
int nGetGlobalID(void) const
Returns the profile's global ID.
Definition profile.cpp:72
CGeom2DIPoint * pPtiGetEndPoint(void)
Returns a pointer to the location of the cell (grid CRS) on which the profile ends.
Definition profile.cpp:96
CGeom2DPoint * pPtGetPointInProfile(int const)
Returns a single point in the profile.
Definition profile.cpp:332
int nGetCoastID(void) const
Returns the profile's coast ID.
Definition profile.cpp:66
void AppendCellInProfile(CGeom2DIPoint const *)
Appends a cell to the profile.
Definition profile.cpp:455
void SetPointInProfile(int const, double const, double const)
Sets a single point in the profile.
Definition profile.cpp:267
int nGetProfileSize(void) const
Returns the number of points in the profile.
Definition profile.cpp:325
CGeom2DIPoint * pPtiGetStartPoint(void)
Returns a pointer to the location of the cell (grid CRS) on which the profile starts.
Definition profile.cpp:84
bool bStartOfCoast(void) const
Returns the switch to indicate whether this is a start-of-coast profile.
Definition profile.cpp:108
void SetEndPoint(CGeom2DIPoint const *)
Sets the the location of the cell (grid CRS) on which the profile ends.
Definition profile.cpp:90
void SetHitLand(bool const)
Sets a switch which indicates whether this profile has hit land.
Definition profile.cpp:138
void SetPointsInProfile(vector< CGeom2DPoint > const *)
Sets all points in the profile.
Definition profile.cpp:261
bool bEndOfCoast(void) const
Returns the switch to indicate whether this is an end-of-coast profile.
Definition profile.cpp:120
void SetProfileDeepWaterWaveAngle(double const)
Sets the deep-water wave orientation for this profile.
Definition profile.cpp:559
void SetTooShort(bool const)
Sets a switch which indicates whether this profile is too short to be useful.
Definition profile.cpp:162
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
double m_dThisIterSWL
The still water level for this timestep (this includes tidal changes and any long-term SWL change)
Definition simulation.h:667
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
CGeom2DIPoint PtiExtCRSToGridRound(CGeom2DPoint const *) const
Transforms a pointer to a CGeom2DPoint in the external CRS to the equivalent CGeom2DIPoint in the ras...
ofstream LogStream
vector< CRWCoast > m_VCoast
The coastline objects.
double m_dCoastNormalLength
Length of the cost-normal profiles, in m.
Definition simulation.h:778
default_random_engine m_Rand[NRNG]
The c++11 random number generators.
void MarkProfilesOnGrid(int const, int &)
For this coastline, marks all coastline-normal profiles (apart from the two 'special' ones at the sta...
static CGeom2DPoint PtChooseEndPoint(int const, CGeom2DPoint const *, CGeom2DPoint const *, double const, double const, double const, double const)
Choose which end point to use for the coastline-normal profile.
int m_nYGridSize
The size of the grid in the y direction.
Definition simulation.h:434
static CGeom2DPoint PtAverage(CGeom2DPoint const *, CGeom2DPoint const *)
Returns a point (external CRS) which is the average of (i.e. is midway between) two other external CR...
int nLocateAndCreateGridEdgeProfile(bool const, int const, int &, int &)
Creates a 'special' profile at each end of a coastline, at the edge of the raster grid....
void MergeProfilesAtFinalLineSegments(int const, CGeomProfile *, CGeomProfile *, int const, int const, double const, double const, double const, double const)
Merges two profiles which intersect at their final (most seaward) line segments, seaward of their poi...
void CheckForIntersectingProfiles(void)
Checks all coastline-normal profiles for intersection, and modifies those that intersect.
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
vector< CGeom2DIPoint > m_VEdgeCell
Edge cells.
static bool bCheckForIntersection(CGeomProfile *const, CGeomProfile *const, int &, int &, double &, double &, double &, double &)
Checks all line segments of a pair of coastline-normal profiles for intersection. If the lines inters...
normal_distribution< double > m_dUnitNormalDist
c++11 unit normal distribution (mean = 0, stdev = 1)
double m_dCoastNormalAvgSpacing
Average spacing of the cost-normal profiles, in m.
Definition simulation.h:775
int nGetCoastNormalEndPoint(int const, int const, int const, CGeom2DPoint const *, double const, CGeom2DPoint *, CGeom2DIPoint *, bool const)
Finds the end point of a coastline-normal line, given the start point on the vector coastline....
void TruncateProfileAndAppendNew(int const, CGeomProfile *, int const, vector< CGeom2DPoint > const *, vector< vector< pair< int, int > > > const *)
Truncate a profile at the point of intersection, and do the same for all its co-incident profiles.
double dExtCRSXToGridX(double const) const
Transforms an X-axis ordinate in the external CRS to the equivalent X-axis ordinate in the raster gri...
int nInsertPointIntoProfilesIfNeededThenUpdate(int const, CGeomProfile *, double const, double const, int const, CGeomProfile *, int const, bool const)
Inserts an intersection point into the profile that is to be retained, if that point is not already p...
void KeepWithinValidGrid(int, int, int &, int &) const
Given two points in the grid CRS (the points assumed not to be coincident), this routine modifies the...
int nCreateAllProfiles(void)
Create coastline-normal profiles for all coastlines. The first profiles are created 'around' the most...
void TruncateOneProfileRetainOtherProfile(int const, CGeomProfile *, CGeomProfile *, double const, double const, int const, int const, bool const)
Truncates one intersecting profile at the point of intersection, and retains the other profile.
double dExtCRSYToGridY(double const) const
Transforms a Y-axis ordinate in the external CRS to the equivalent Y-axis ordinate in the raster grid...
double m_dCoastNormalRandSpacingFactor
Random factor for spacing of along-coast normals.
Definition simulation.h:904
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,...
int nCreateProfile(int const, int const, int const, int const, int &, bool const, CGeom2DIPoint const *)
Creates a single coastline-normal profile (which may be an intervention profile)
void CreateRasterizedProfile(int const, CGeomProfile *, CGeom2DIPoint const *, CGeom2DIPoint const *, vector< CGeom2DIPoint > *, vector< bool > *, bool &, bool &, bool &, bool &)
Given a pointer to a coastline-normal profile, returns an output vector of cells which are 'under' ev...
static void AppendEnsureNoGap(vector< CGeom2DIPoint > *, CGeom2DIPoint const *)
Appends a CGeom2DIPoint to a vector<CGeom2DIPoint>, making sure that the new end point touches the pr...
Definition utils.cpp:2317
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
double m_dCellSide
Length of a cell side (in external CRS units)
Definition simulation.h:613
int m_nCoastNormalAvgSpacing
Average spacing between cost normals, measured in cells.
Definition simulation.h:452
double m_dDepthOfClosure
Depth of closure (in m) TODO 007 can be calculated using Hallermeier, R.J. (1978) or Birkemeier (1985...
Definition simulation.h:772
int nCheckAllProfiles(void)
Check all coastline-normal profiles and modify the profiles if they intersect, then mark valid profil...
void LocateAndCreateProfiles(int const, int &, int &, int const, vector< bool > *, vector< pair< int, double > > const *)
For a single coastline, locate the start points for all coastline-normal profiles (except the grid-ed...
vector< int > m_VEdgeCellEdge
The grid edge that each edge cell belongs to.
This file contains global definitions for CoastalME.
int const INT_NODATA
Definition cme.h:362
T tMin(T a, T b)
Definition cme.h:1136
double const TOLERANCE
Definition cme.h:698
int const RTN_ERR_COAST_CANT_FIND_EDGE_CELL
Definition cme.h:635
string const ERR
Definition cme.h:775
int const DIRECTION_DOWNCOAST
Definition cme.h:392
int const LOG_FILE_MIDDLE_DETAIL
Definition cme.h:377
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 RTN_ERR_PROFILE_END_INSUFFICIENT_DEPTH
Definition cme.h:605
int const PROFILE_CHECK_DIST_FROM_COAST
Definition cme.h:675
string const WARN
Definition cme.h:776
int const RTN_ERR_NO_PROFILES_2
Definition cme.h:607
int const PROFILE_CHECK_ALONG_COAST_MULTIPLIER
Definition cme.h:674
int const RTN_ERR_PROFILE_ENDPOINT_IS_INLAND
Definition cme.h:603
int const LOG_FILE_ALL
Definition cme.h:379
int const RTN_OK
Definition cme.h:577
bool const ACCEPT_SHORT_PROFILES
Definition cme.h:345
int const RTN_ERR_NO_PROFILES_1
Definition cme.h:606
int const RTN_ERR_CANNOT_INSERT_POINT
Definition cme.h:626
int const DIRECTION_UPCOAST
Definition cme.h:393
string strDblToStr(const T &t)
Definition cme.h:1162
int const RTN_ERR_NO_SOLUTION_FOR_ENDPOINT
Definition cme.h:604
int const RIGHT_HANDED
Definition cme.h:397
int const RTN_ERR_PROFILE_ENDPOINT_AT_GRID_EDGE
Definition cme.h:602
T tAbs(T a)
Definition cme.h:1148
int const LF_CAT_INTERVENTION
Definition cme.h:431
int const LEFT_HANDED
Definition cme.h:398
Contains CRWCoast definitions.
bool bCurvaturePairCompareDescending(const pair< int, double > &prLeft, const pair< int, double > &prRight)
Function used to sort coastline curvature values when locating start points of normal profiles.
Contains CSimulation definitions.
int nRound(double const d)
Version of the above that returns an int.