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