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