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