1 /*!
2 *
3 * \file create_profiles.cpp
4 * \brief Creates profiles which are approximately normal to the coastline, these will become inter-polygon boundaries
5 * \details TODO A more detailed description of these routines.
6 * \author David Favis-Mortlock
7 * \author Andres Payo
8 * \author Jim Hall
9 * \date 2017
10 * \copyright GNU General Public License
11 *
12 */
13
14 /*==============================================================================================================================
15
16 This file is part of CliffMetrics, the Coastal Modelling Environment.
17
18 CliffMetrics 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.
19
20 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.
21
22 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.
23
24 ==============================================================================================================================*/
25 // #include <assert.h>
26 #include <cmath>
27
28 #include <iostream>
29 using std::cout;
30 using std::cerr;
31 using std::endl;
32 using std::ios;
33
34 #include <iomanip>
35 using std::setiosflags;
36 using std::setprecision;
37
38 #include <algorithm>
39
40 #include <utility>
41 using std::pair;
42 using std::make_pair;
43
44 #include "cliffmetrics.h"
45 #include "delineation.h"
46 #include "coast.h"
47
48
49 /*===============================================================================================================================
50
51 Function used to sort coastline curvature values
52
53 ===============================================================================================================================*/
bCurvaturePairCompare(const pair<int,double> & prLeft,const pair<int,double> & prRight)54 bool bCurvaturePairCompare(const pair<int, double> &prLeft, const pair<int, double> &prRight)
55 {
56 // Sort in descending order (i.e. most concave first)
57 return prLeft.second > prRight.second;
58 }
59
60
61 /*===============================================================================================================================
62
63 Create profiles normal to the coastline, modifies these if they intersect, then puts the profiles onto the raster grid
64
65 ===============================================================================================================================*/
nCreateAllProfilesAndCheckForIntersection(void)66 int CDelineation::nCreateAllProfilesAndCheckForIntersection(void)
67 {
68 // Create all coastline-normal profiles, in coastline-concave-curvature sequence i.e. the first profiles are created 'around' the most concave bits of coast. An index is also created which allows profiles to be accessed in along-cost sequence
69 int nRet = nCreateAllProfiles();
70 if (nRet != RTN_OK)
71 return nRet;
72
73 // 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
74 // nRet = nModifyAllIntersectingProfiles();
75 // if (nRet != RTN_OK)
76 // return nRet;
77
78 // Put all valid coastline-normal profiles onto the raster grid: but if the profile is not long enough, or crosses a coastline or hits dry land, then mark the profile as invalid
79 nRet = nPutAllProfilesOntoGrid();
80 if (nRet != RTN_OK)
81 return nRet;
82
83 return RTN_OK;
84 }
85
86
87 /*===============================================================================================================================
88
89 Create coastline-normal profiles for all coastlines: first at a limited number of 'cape' positions, then at locations of greatest concave curvature of the vector coastline. Finally, grid-edge profiles are created
90
91 ===============================================================================================================================*/
nCreateAllProfiles(void)92 int CDelineation::nCreateAllProfiles(void)
93 {
94 // For each coastline, search for coastline points from which to start a normal profile
95 for (unsigned int nCoast = 0; nCoast < m_VCoast.size(); nCoast++)
96 {
97 int
98 nProfile = -1,
99 nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
100
101 // 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
102 vector<pair<int, double> > prVCurvature;
103 vector<double> dVCurvature;
104 for (int nCoastPoint = 0; nCoastPoint < nCoastSize; nCoastPoint++)
105 {
106 double dCurvature = m_VCoast[nCoast].dGetCurvature(nCoastPoint);
107 prVCurvature.push_back(make_pair(nCoastPoint, dCurvature));
108 dVCurvature.push_back(dCurvature);
109 }
110
111 // And create a vector of the same length, to mark coastline points which have been searched
112 vector<bool> bVPointSearched(nCoastSize, false);
113
114 // Create Normals profiles at every coast points on the coastline
115 while (true)
116 {
117 // How many coastline points are still to be searched?
118 int nStillToSearch = 0;
119 for (int n = 0; n < nCoastSize; n++)
120 {
121 if (! bVPointSearched[n])
122 nStillToSearch++;
123 }
124 // Are there any coastline points still to be searched?
125 if (nStillToSearch == 0)
126 {// Nope, we are done here
127 break;
128 }
129 // Look along the vector of pairs starting at the most convex end
130 for (int n = 0; n < nCoastSize; n++)
131 {
132 // Curvature at this coastline point is more convex (i.e. less than) the convexity threshold, so this is a potential location for a 'cape' profile
133 int nCapePoint = prVCurvature[n].first;
134 if (! bVPointSearched[nCapePoint])
135 {
136 // We have not already searched this point, so try putting a 'cape' profile here
137 int nRet = nCreateProfile(nCoast, nCapePoint, nProfile);
138 bVPointSearched[nCapePoint] = true;
139
140 if (nRet != RTN_OK)
141 // This cape profile is no good (has hit coast, or hit dry land, etc.) so forget about it
142 continue;
143 }
144 }
145 }
146
147
148 // Did we fail to create any normal profiles? If so, quit
149 if (nProfile < 0)
150 {
151 string strErr = ERR + ": could not create profiles for coastline " + tToStr(nCoast) ;
152 strErr += ". Check the initial SWL";
153 strErr += "\n";
154 cerr << strErr;
155 LogStream << strErr;
156 // return RTN_ERR_BADPROFILE;
157 }
158
159 // Create an index to the profiles in along-coast sequence
160 m_VCoast[nCoast].CreateAlongCoastlineProfileIndex();
161
162 }
163
164 return RTN_OK;
165 }
166
167
168 /*==============================================================================================================================
169
170 Creates a coastline-normal profile
171
172 ===============================================================================================================================*/
nCreateProfile(int const nCoast,int const nProfileStartPoint,int & nProfile)173 int CDelineation::nCreateProfile(int const nCoast, int const nProfileStartPoint, int& nProfile)
174 {
175 // 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)
176 int nCoastSize = m_VCoast[nCoast].nGetCoastlineSize();
177
178 C2DPoint PtStart; // PtStart has coordinates in external CRS
179 PtStart.SetX(dGridCentroidXToExtCRSX(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nProfileStartPoint)->nGetX()));
180 PtStart.SetY(dGridCentroidYToExtCRSY(m_VCoast[nCoast].pPtiGetCellMarkedAsCoastline(nProfileStartPoint)->nGetY()));
181
182 C2DPoint PtSeaEnd; // Also in external CRS
183 C2DPoint PtLandEnd; // Also in external CRS
184 if (nGetCoastNormalEndPoint(nCoast, nProfileStartPoint, nCoastSize, &PtStart, m_dCoastNormalLength, &PtSeaEnd, &PtLandEnd) != RTN_OK)
185 // Could not solve end-point equation, or profile end point is off-grid, so forget about this profile
186 return RTN_ERR_OFFGRID_ENDPOINT;
187
188 // No problems, so create the new profile
189 m_VCoast[nCoast].AppendProfile(nProfileStartPoint, ++nProfile);
190
191 // And create the profile's coastline-normal vector (start and end points are in external CRS)
192 vector<C2DPoint> VNormal;
193 VNormal.push_back(PtStart);
194 VNormal.push_back(PtSeaEnd); // to draw the profiles LANDWARDS!!!
195 //VNormal.push_back(PtLandEnd); // to draw the profiles SEAWARDS!!!!
196
197 CProfile* pProfile = m_VCoast[nCoast].pGetProfile(nProfile);
198 pProfile->SetAllPointsInProfile(&VNormal);
199
200 // Create the profile's CMultiLine then set nProfile as the only co-incident profile of the only line segment
201 pProfile->AppendLineSegment();
202 pProfile->AppendCoincidentProfileToLineSegments(make_pair(nProfile, 0));
203
204 // LogStream << setiosflags(ios::fixed) << m_ulTimestep << ": profile " << nProfile << " created at point " << nProfileStartPoint << " from [" << PtStart.dGetX() << "][" << PtStart.dGetY() << "] to [" << PtEnd.dGetX() << "][" << PtEnd.dGetY() << "]" << endl;
205 return RTN_OK;
206 }
207
208
209 /*==============================================================================================================================
210
211 Finds the end point of a coastline-normal line, given the start point on the vector coastline. All co-ordinates are in the external CRS
212
213 ===============================================================================================================================*/
nGetCoastNormalEndPoint(int const nCoast,int const nStartCoastPoint,int const nCoastSize,C2DPoint * const pPtStart,double const dLineLength,C2DPoint * pPtSeaEnd,C2DPoint * pPtLandEnd)214 int CDelineation::nGetCoastNormalEndPoint(int const nCoast, int const nStartCoastPoint, int const nCoastSize, C2DPoint* const pPtStart, double const dLineLength, C2DPoint* pPtSeaEnd, C2DPoint* pPtLandEnd)
215 {
216 // If at beginning or end of coast, need special treatment for points before and points after
217 int nCoastPointBefore = tMax(nStartCoastPoint-1, 0);
218 int nCoastPointAfter = tMin(nStartCoastPoint+1, nCoastSize-1);
219
220 // Get the y = a * x + b equation of the straight line linking the coastline points before and after 'this' coastline point
221 C2DPoint PtBefore = *m_VCoast[nCoast].pPtGetVectorCoastlinePoint(nCoastPointBefore); // PtBefore has coordinates in external CRS
222 C2DPoint PtAfter = *m_VCoast[nCoast].pPtGetVectorCoastlinePoint(nCoastPointAfter); // PtAfter has coordinates in external CRS
223
224 // For this linking line, slope a = (y2 - y1) / (x2 - x1)
225 double dYDiff = PtAfter.dGetY() - PtBefore.dGetY();
226 double dXDiff = PtAfter.dGetX() - PtBefore.dGetX();
227
228 double dXEnd1 = 0, dXEnd2 = 0, dYEnd1 = 0, dYEnd2 = 0;
229
230 if (bFPIsEqual(dYDiff, 0, TOLERANCE))
231 {
232 // 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
233 dXEnd1 = dXEnd2 = pPtStart->dGetX();
234 dYEnd1 = pPtStart->dGetY() + dLineLength;
235 dYEnd2 = pPtStart->dGetY() - dLineLength;
236 }
237 else if (bFPIsEqual(dXDiff, 0, TOLERANCE))
238 {
239 // 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
240 dYEnd1 = dYEnd2 = pPtStart->dGetY();
241 dXEnd1 = pPtStart->dGetX() + dLineLength;
242 dXEnd2 = pPtStart->dGetX() - dLineLength;
243 }
244 else
245 {
246 // 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
247 double dA = dYDiff / dXDiff;
248
249 // Now calculate the equation of the straight line which is perpendicular to this linking line
250 double dAPerp = -1 / dA;
251 double dBPerp = pPtStart->dGetY() - (dAPerp * pPtStart->dGetX());
252
253 // 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)
254 double dQuadA = 1 + (dAPerp * dAPerp);
255 double dQuadB = 2 * ((dBPerp * dAPerp) - (dAPerp * pPtStart->dGetY()) - pPtStart->dGetX());
256 double dQuadC = ((pPtStart->dGetX() * pPtStart->dGetX()) + (pPtStart->dGetY() * pPtStart->dGetY()) + (dBPerp * dBPerp) - (2 * pPtStart->dGetY() * dBPerp) - (dLineLength * dLineLength));
257
258 // Solve for x and y using the quadratic formula x = (−B ± sqrt(B^2 − 4AC)) / 2A
259 double dDiscriminant = (dQuadB * dQuadB) - (4 * dQuadA * dQuadC);
260 if (dDiscriminant < 0)
261 {
262 LogStream << ERR << "timestep " << m_ulTimestep << ": discriminant < 0 when finding profile end point on coastline " << nCoast << ", from coastline point " << nStartCoastPoint << "), ignored" << endl;
263 return RTN_ERR_BADENDPOINT;
264 }
265
266 dXEnd1 = (-dQuadB + sqrt(dDiscriminant)) / (2 * dQuadA);
267 dYEnd1 = (dAPerp * dXEnd1) + dBPerp;
268 dXEnd2 = (-dQuadB - sqrt(dDiscriminant)) / (2 * dQuadA);
269 dYEnd2 = (dAPerp * dXEnd2) + dBPerp;
270 }
271
272 // We have two possible solutions, so decide which of the two endpoints to use then create the profile end-point (coordinates in external CRS)
273 int nSeaHand = m_VCoast[nCoast].nGetSeaHandedness(); // Assumes handedness is either 0 or 1 (i.e. not -1)
274
275 *pPtLandEnd = PtChooseLandEndPoint(nSeaHand, &PtBefore, &PtAfter, dXEnd1, dYEnd1, dXEnd2, dYEnd2);
276 *pPtSeaEnd = PtChooseSeaEndPoint(nSeaHand, &PtBefore, &PtAfter, dXEnd1, dYEnd1, dXEnd2, dYEnd2);
277
278 // cout<< pPtSeaEnd->dGetX() << ", " << pPtSeaEnd->dGetY() << endl;
279 // cout<< pPtLandEnd->dGetX() << ", " << pPtLandEnd->dGetY() << endl;
280 // cout << endl;
281
282 // Check that pPtSeaEnd is not off the grid. Note that ptSeaEnd is NOT (necessarily) a cell centroid
283 if (! bIsWithinGrid(static_cast<int>(dRound(dExtCRSXToGridX(pPtSeaEnd->dGetX()))), static_cast<int>(dRound(dExtCRSYToGridY(pPtSeaEnd->dGetY())))))
284 {
285 // LogStream << WARN << "timestep " << m_ulTimestep << ": profile endpoint is outside grid for coastline " << nCoast << ", from coastline point " << nStartCoastPoint << "), ignored" << endl;
286 return RTN_ERR_OFFGRID_ENDPOINT;
287 }
288
289 // Check that pPtLandEnd is not off the grid. Note that ptLandEnd is NOT (necessarily) a cell centroid
290 if (! bIsWithinGrid(static_cast<int>(dRound(dExtCRSXToGridX(pPtLandEnd->dGetX()))), static_cast<int>(dRound(dExtCRSYToGridY(pPtLandEnd->dGetY())))))
291 {
292 // LogStream << WARN << "timestep " << m_ulTimestep << ": profile endpoint is outside grid for coastline " << nCoast << ", from coastline point " << nStartCoastPoint << "), ignored" << endl;
293 return RTN_ERR_OFFGRID_ENDPOINT;
294 }
295 return RTN_OK;
296 }
297
298 /*==============================================================================================================================
299
300 Choose which end point to use for the coastline-normal profile at the LAND side
301
302 ===============================================================================================================================*/
PtChooseLandEndPoint(int const nHand,C2DPoint * const PtBefore,C2DPoint * const PtAfter,double const dXEnd1,double const dYEnd1,double const dXEnd2,double const dYEnd2)303 C2DPoint CDelineation::PtChooseLandEndPoint(int const nHand, C2DPoint* const PtBefore, C2DPoint* const PtAfter, double const dXEnd1, double const dYEnd1, double const dXEnd2, double const dYEnd2)
304 {
305 C2DPoint PtChosen;
306
307 if (nHand == LEFT_HANDED) // sea is to the left, so land is to the right of the linking line
308 {
309 // The land is to the right of the linking line. So which way is the linking line oriented? First check the N-S component
310 if (PtAfter->dGetY() > PtBefore->dGetY())
311 {
312 // We are going N to S and the land is to the right, so the normal endpoint is to the W
313 if (dXEnd1 < dXEnd2)
314 {
315 PtChosen.SetX(dXEnd1);
316 PtChosen.SetY(dYEnd1);
317 }
318 else
319 {
320 PtChosen.SetX(dXEnd2);
321 PtChosen.SetY(dYEnd2);
322 }
323 }
324 else if (PtAfter->dGetY() < PtBefore->dGetY())
325 {
326 // We are going S to N and the land is to the right, so the normal endpoint is to the E
327 if (dXEnd1 > dXEnd2)
328 {
329 PtChosen.SetX(dXEnd1);
330 PtChosen.SetY(dYEnd1);
331 }
332 else
333 {
334 PtChosen.SetX(dXEnd2);
335 PtChosen.SetY(dYEnd2);
336 }
337 }
338 else
339 {
340 // No N-S component i.e. the linking line is exactly W-E. So check the W-E component
341 if (PtAfter->dGetX() > PtBefore->dGetX())
342 {
343 // We are going W to E and the land is to the right, so the normal endpoint is to the S (note that the origin of the grid is to the top left)
344 if (dYEnd1 > dYEnd2)
345 {
346 PtChosen.SetX(dXEnd1);
347 PtChosen.SetY(dYEnd1);
348 }
349 else
350 {
351 PtChosen.SetX(dXEnd2);
352 PtChosen.SetY(dYEnd2);
353 }
354 }
355 else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident
356 {
357 // We are going E to W and the land is to the right, so the normal endpoint is to the N (note that the origin of the grid is to the top left)
358 if (dYEnd1 < dYEnd2)
359 {
360 PtChosen.SetX(dXEnd1);
361 PtChosen.SetY(dYEnd1);
362 }
363 else
364 {
365 PtChosen.SetX(dXEnd2);
366 PtChosen.SetY(dYEnd2);
367 }
368 }
369 }
370 }
371 else
372 {
373 // The sea is to the right (land is to the left) of the linking line. So which way is the linking line oriented? First check the N-S component
374 if (PtAfter->dGetY() > PtBefore->dGetY())
375 {
376 // We are going N to S and the land is to the left, so the normal endpoint is to the E
377 if (dXEnd1 > dXEnd2)
378 {
379 PtChosen.SetX(dXEnd1);
380 PtChosen.SetY(dYEnd1);
381 }
382 else
383 {
384 PtChosen.SetX(dXEnd2);
385 PtChosen.SetY(dYEnd2);
386 }
387 }
388 else if (PtAfter->dGetY() < PtBefore->dGetY())
389 {
390 // We are going S to N and the land is to the left, so the normal endpoint is to the W
391 if (dXEnd1 < dXEnd2)
392 {
393 PtChosen.SetX(dXEnd1);
394 PtChosen.SetY(dYEnd1);
395 }
396 else
397 {
398 PtChosen.SetX(dXEnd2);
399 PtChosen.SetY(dYEnd2);
400 }
401 }
402 else
403 {
404 // No N-S component i.e. the linking line is exactly W-E. So check the W-E component
405 if (PtAfter->dGetX() > PtBefore->dGetX())
406 {
407 // We are going W to E and the land is to the left, so the normal endpoint is to the N (note that the origin of the grid is to the top left)
408 if (dYEnd1 < dYEnd2)
409 {
410 PtChosen.SetX(dXEnd1);
411 PtChosen.SetY(dYEnd1);
412 }
413 else
414 {
415 PtChosen.SetX(dXEnd2);
416 PtChosen.SetY(dYEnd2);
417 }
418 }
419 else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident
420 {
421 // We are going E to W and the land is to the left, so the normal endpoint is to the S (note that the origin of the grid is to the top left)
422 if (dYEnd1 > dYEnd2)
423 {
424 PtChosen.SetX(dXEnd1);
425 PtChosen.SetY(dYEnd1);
426 }
427 else
428 {
429 PtChosen.SetX(dXEnd2);
430 PtChosen.SetY(dYEnd2);
431 }
432 }
433 }
434 }
435
436 return PtChosen;
437 }
438
439 /*==============================================================================================================================
440
441 Choose which end point to use for the coastline-normal profile at the SEA side
442
443 ===============================================================================================================================*/
PtChooseSeaEndPoint(int const nHand,C2DPoint * const PtBefore,C2DPoint * const PtAfter,double const dXEnd1,double const dYEnd1,double const dXEnd2,double const dYEnd2)444 C2DPoint CDelineation::PtChooseSeaEndPoint(int const nHand, C2DPoint* const PtBefore, C2DPoint* const PtAfter, double const dXEnd1, double const dYEnd1, double const dXEnd2, double const dYEnd2)
445 {
446 C2DPoint PtChosen;
447
448 if (nHand == RIGHT_HANDED)
449 {
450 // The sea is to the right of the linking line. So which way is the linking line oriented? First check the N-S component
451 if (PtAfter->dGetY() > PtBefore->dGetY())
452 {
453 // We are going N to S and the sea is to the right, so the normal endpoint is to the W
454 if (dXEnd1 < dXEnd2)
455 {
456 PtChosen.SetX(dXEnd1);
457 PtChosen.SetY(dYEnd1);
458 }
459 else
460 {
461 PtChosen.SetX(dXEnd2);
462 PtChosen.SetY(dYEnd2);
463 }
464 }
465 //else if (bFPIsEqual(PtAfter->dGetY(), PtBefore->dGetY(), TOLERANCE))
466 else if (PtAfter->dGetY() < PtBefore->dGetY())
467 {
468 // We are going S to N and the sea is to the right, so the normal endpoint is to the E
469 if (dXEnd1 > dXEnd2)
470 {
471 PtChosen.SetX(dXEnd1);
472 PtChosen.SetY(dYEnd1);
473 }
474 else
475 {
476 PtChosen.SetX(dXEnd2);
477 PtChosen.SetY(dYEnd2);
478 }
479 }
480 else
481 {
482 // No N-S component i.e. the linking line is exactly W-E. So check the W-E component
483 if (PtAfter->dGetX() > PtBefore->dGetX())
484 {
485 // We are going W to E and the sea is to the right, so the normal endpoint is to the S (note that the origin of the grid is to the top left)
486 if (dYEnd1 > dYEnd2)
487 {
488 PtChosen.SetX(dXEnd1);
489 PtChosen.SetY(dYEnd1);
490 }
491 else
492 {
493 PtChosen.SetX(dXEnd2);
494 PtChosen.SetY(dYEnd2);
495 }
496 }
497 else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident
498 {
499 // We are going E to W and the sea is to the right, so the normal endpoint is to the N (note that the origin of the grid is to the top left)
500 if (dYEnd1 < dYEnd2)
501 {
502 PtChosen.SetX(dXEnd1);
503 PtChosen.SetY(dYEnd1);
504 }
505 else
506 {
507 PtChosen.SetX(dXEnd2);
508 PtChosen.SetY(dYEnd2);
509 }
510 }
511 }
512 }
513 else
514 {
515 // The sea is to the left of the linking line. So which way is the linking line oriented? First check the N-S component
516 if (PtAfter->dGetY() > PtBefore->dGetY())
517 {
518 // We are going N to S and the sea is to the left, so the normal endpoint is to the E
519 if (dXEnd1 > dXEnd2)
520 {
521 PtChosen.SetX(dXEnd1);
522 PtChosen.SetY(dYEnd1);
523 }
524 else
525 {
526 PtChosen.SetX(dXEnd2);
527 PtChosen.SetY(dYEnd2);
528 }
529 }
530 //else if (bFPIsEqual(PtAfter->dGetY(), PtBefore->dGetY(), TOLERANCE))
531 else if (PtAfter->dGetY() < PtBefore->dGetY())
532 {
533 // We are going S to N and the sea is to the left, so the normal endpoint is to the W
534 if (dXEnd1 < dXEnd2)
535 {
536 PtChosen.SetX(dXEnd1);
537 PtChosen.SetY(dYEnd1);
538 }
539 else
540 {
541 PtChosen.SetX(dXEnd2);
542 PtChosen.SetY(dYEnd2);
543 }
544 }
545 else
546 {
547 // No N-S component i.e. the linking line is exactly W-E. So check the W-E component
548 if (PtAfter->dGetX() > PtBefore->dGetX())
549 {
550 // We are going W to E and the sea is to the left, so the normal endpoint is to the N (note that the origin of the grid is to the top left)
551 if (dYEnd1 < dYEnd2)
552 {
553 PtChosen.SetX(dXEnd1);
554 PtChosen.SetY(dYEnd1);
555 }
556 else
557 {
558 PtChosen.SetX(dXEnd2);
559 PtChosen.SetY(dYEnd2);
560 }
561 }
562 else // Do not check for (PtAfter->dGetX() == PtBefore->dGetX()), since this would mean the two points are co-incident
563 {
564 // We are going E to W and the sea is to the left, so the normal endpoint is to the S (note that the origin of the grid is to the top left)
565 if (dYEnd1 > dYEnd2)
566 {
567 PtChosen.SetX(dXEnd1);
568 PtChosen.SetY(dYEnd1);
569 }
570 else
571 {
572 PtChosen.SetX(dXEnd2);
573 PtChosen.SetY(dYEnd2);
574 }
575 }
576 }
577 }
578
579 return PtChosen;
580 }
581
582
583 /*==============================================================================================================================
584
585 Checks all coastline-normal profiles for intersection, and modifies those that intersect
586
587 ===============================================================================================================================*/
nModifyAllIntersectingProfiles(void)588 int CDelineation::nModifyAllIntersectingProfiles(void)
589 {
590 // Do once for every coastline object
591 int nCoastLines = m_VCoast.size();
592 for (int nCoast = 0; nCoast < nCoastLines; nCoast++)
593 {
594 int nNumProfiles = m_VCoast[nCoast].nGetNumProfiles();
595
596 // Go along the coast, looking at profiles which are increasingly distant from the first profile
597 int nMaxDist = nNumProfiles / 2; // Arbitrary
598 for (int nDist = 1; nDist < nMaxDist; nDist++)
599 {
600 // Do once for every profile
601 for (int nFirst = 0; nFirst < nNumProfiles; nFirst++)
602 {
603 // In coastline curvature sequence
604 int nFirstProfile = m_VCoast[nCoast].nGetProfileAtAlongCoastlinePosition(nFirst);
605 if (nFirstProfile < 0)
606 // Not found
607 return RTN_ERR_BAD_INDEX;
608
609 // LogStream << m_ulTimestep << ": nFirst = " << nFirst << " nFirstProfile = " << nFirstProfile << endl;
610
611 // Don't modify the start- or end-of coastline normals
612 CProfile* pFirstProfile = m_VCoast[nCoast].pGetProfile(nFirstProfile);
613 if ((pFirstProfile->bStartOfCoast()) || (pFirstProfile->bEndOfCoast()))
614 continue;
615
616 // Do this in alternate directions: first down-coast (in the direction of increasing coast point numbers) then up-coast
617 for (int nDirection = DIRECTION_DOWNCOAST; nDirection <= DIRECTION_UPCOAST; nDirection++)
618 {
619 int nSecond;
620 if (nDirection == DIRECTION_DOWNCOAST)
621 nSecond = nFirst + nDist;
622 else
623 nSecond = nFirst - nDist;
624
625 if ((nSecond < 0) || (nSecond > nNumProfiles-1))
626 // Make sure that we don't go beyond the ends of the along-coast index
627 continue;
628
629 int nSecondProfile = m_VCoast[nCoast].nGetProfileAtAlongCoastlinePosition(nSecond);
630
631 // LogStream << m_ulTimestep << ": " << (nDirection == DIRECTION_DOWNCOAST ? "down" : "up") << "-coast search, nFirst = " << nFirst << " nSecond = " << nSecond << " (profiles " << nFirstProfile << " and " << nSecondProfile << ")" << endl;
632
633 // Only check these profiles for intersection if both are problem-free
634 CProfile* pSecondProfile = m_VCoast[nCoast].pGetProfile(nSecondProfile);
635 if (! (pFirstProfile->bProfileOK()) || (! pSecondProfile->bProfileOK()))
636 continue;
637
638 // 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)
639 if ((pFirstProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nSecondProfile)) || (pSecondProfile->bFindProfileInCoincidentProfilesOfLastLineSegment(nFirstProfile)))
640 continue;
641
642 // OK go for it
643 int
644 nProf1LineSeg = 0,
645 nProf2LineSeg = 0;
646 double
647 dIntersectX = 0,
648 dIntersectY = 0,
649 dAvgEndX = 0,
650 dAvgEndY = 0;
651
652 if (bCheckForIntersection(pFirstProfile, pSecondProfile, nProf1LineSeg, nProf2LineSeg, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY))
653 {
654 // The profiles intersect. 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)?
655 int nPoint = -1;
656 if (pFirstProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint))
657 {
658 // LogStream << m_ulTimestep << ": ^^^^ profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nFirstProfile << " as point " << nPoint << endl;
659
660 // Truncate the second profile and merge it with the first profile
661 TruncateOneProfileRetainOtherProfile(nCoast, nSecondProfile, nFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, true);
662 }
663
664 // Is the point of intersection already present in the second profile?
665 else if (pSecondProfile->bIsPointInProfile(dIntersectX, dIntersectY, nPoint))
666 {
667 // LogStream << m_ulTimestep << ": ^^^^ profiles " << nFirstProfile << " and " << nSecondProfile << " intersect, but point {" << dIntersectX << ", " << dIntersectY << "} is already present in profile " << nSecondProfile << " as point " << nPoint << endl;
668
669 // Truncate the first profile and merge it with the second profile
670 TruncateOneProfileRetainOtherProfile(nCoast, nFirstProfile, nSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, true);
671 }
672
673 else
674 {
675 // The point of intersection is not already present in either profile, so get the number of line segments of each profile
676 int
677 nFirstProfileLineSegments = pFirstProfile->nGetNumLineSegments(),
678 nSecondProfileLineSegments = pSecondProfile->nGetNumLineSegments();
679
680 // assert(nProf1LineSeg < nFirstProfileLineSegments);
681 // assert(nProf2LineSeg < nSecondProfileLineSegments);
682
683 // Next check whether the point of intersection is on the final line segment of both profiles
684 if ((nProf1LineSeg == (nFirstProfileLineSegments-1)) && (nProf2LineSeg == (nSecondProfileLineSegments-1)))
685 {
686 // Yes, the point of intersection is on the final line segment of both profiles, so merge the profiles seaward of the point of intersection
687 MergeProfilesAtFinalLineSegments(nCoast, nFirstProfile, nSecondProfile, nFirstProfileLineSegments, nSecondProfileLineSegments, dIntersectX, dIntersectY, dAvgEndX, dAvgEndY);
688
689 // LogStream << m_ulTimestep << ": " << ((nDirection == DIRECTION_DOWNCOAST) ? "down" : "up") << "-coast search, end-segment intersection between profiles {" << nFirstProfile << "} and {" << nSecondProfile << "} at [" << dIntersectX << ", " << dIntersectY << "] in line segment [" << nProf1LineSeg << "] of " << nFirstProfileLineSegments << ", and line segment [" << nProf2LineSeg << "] of " << nSecondProfileLineSegments << ", respectively" << endl;
690 }
691 else
692 {
693 // 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
694 // LogStream << m_ulTimestep << ": " << ((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;
695
696 // Decide which profile to truncate, and which to retain
697 if (nFirstProfileLineSegments > nSecondProfileLineSegments)
698 // Truncate the second profile, since it has a smaller number of line segments
699 TruncateOneProfileRetainOtherProfile(nCoast, nSecondProfile, nFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false);
700
701 else if (nFirstProfileLineSegments < nSecondProfileLineSegments)
702 // Truncate the first profile, since it has a smaller number of line segments
703 TruncateOneProfileRetainOtherProfile(nCoast, nFirstProfile, nSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false);
704
705 else
706 {
707 // Both profiles have the same number of line segments, so choose randomnly
708 if (dGetRand0d1() >= 0.5)
709 TruncateOneProfileRetainOtherProfile(nCoast, nFirstProfile, nSecondProfile, dIntersectX, dIntersectY, nProf1LineSeg, nProf2LineSeg, false);
710 else
711 TruncateOneProfileRetainOtherProfile(nCoast, nSecondProfile, nFirstProfile, dIntersectX, dIntersectY, nProf2LineSeg, nProf1LineSeg, false);
712 }
713 }
714 }
715
716 // int
717 // nProfile1NumSegments = pFirstProfile->nGetNumLineSegments(),
718 // nProfile2NumSegments = pSecondProfile->nGetNumLineSegments(),
719 // nProfile1Size = pFirstProfile->nGetProfileSize(),
720 // nProfile2Size = pSecondProfile->nGetProfileSize();
721
722 // assert(pFirstProfile->nGetNumLineSegments() > 0);
723 // assert(pSecondProfile->nGetNumLineSegments() > 0);
724 // assert(nProfile1Size == nProfile1NumSegments+1);
725 // assert(nProfile2Size == nProfile2NumSegments+1);
726 }
727 }
728 }
729 }
730 }
731
732 return RTN_OK;
733 }
734
735
736 /*==============================================================================================================================
737
738 Checks all line segments of a pair of coastline-normal profiles for intersection. If the lines intersect, returns true with numbers of the line segments at which intersection occurs in nProfile1LineSegment and nProfile1LineSegment, the intersection point in dXIntersect and dYIntersect, and the 'average' seaward endpoint of the two intersecting profiles at dXAvgEnd and dYAvgEnd
739
740 ===============================================================================================================================*/
bCheckForIntersection(CProfile * const pVProfile1,CProfile * const pVProfile2,int & nProfile1LineSegment,int & nProfile2LineSegment,double & dXIntersect,double & dYIntersect,double & dXAvgEnd,double & dYAvgEnd)741 bool CDelineation::bCheckForIntersection(CProfile* const pVProfile1, CProfile* const pVProfile2, int& nProfile1LineSegment, int& nProfile2LineSegment, double& dXIntersect, double& dYIntersect, double& dXAvgEnd, double& dYAvgEnd)
742 {
743 // For both profiles, look at all line segments
744 int
745 nProfile1NumSegments = pVProfile1->nGetNumLineSegments(),
746 nProfile2NumSegments = pVProfile2->nGetNumLineSegments();
747 // nProfile1Size = pVProfile1->nGetProfileSize(),
748 // nProfile2Size = pVProfile2->nGetProfileSize();
749
750 // assert(nProfile1Size == nProfile1NumSegments+1);
751 // assert(nProfile2Size == nProfile2NumSegments+1);
752
753 for (int i = 0; i < nProfile1NumSegments; i++)
754 {
755 for (int j = 0; j < nProfile2NumSegments; j++)
756 {
757 // In external coordinates
758 double
759 dX1 = pVProfile1->pPtVGetPoints()->at(i).dGetX(),
760 dY1 = pVProfile1->pPtVGetPoints()->at(i).dGetY(),
761 dX2 = pVProfile1->pPtVGetPoints()->at(i+1).dGetX(),
762 dY2 = pVProfile1->pPtVGetPoints()->at(i+1).dGetY();
763
764 double
765 dX3 = pVProfile2->pPtVGetPoints()->at(j).dGetX(),
766 dY3 = pVProfile2->pPtVGetPoints()->at(j).dGetY(),
767 dX4 = pVProfile2->pPtVGetPoints()->at(j+1).dGetX(),
768 dY4 = pVProfile2->pPtVGetPoints()->at(j+1).dGetY();
769
770 // 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")
771 double
772 dDiffX1 = dX2 - dX1,
773 dDiffY1 = dY2 - dY1,
774 dDiffX2 = dX4 - dX3,
775 dDiffY2 = dY4 - dY3;
776
777 double
778 dS = -999,
779 dT = -999,
780 dTmp = 0;
781
782 dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2;
783 if (dTmp != 0)
784 dS = (-dDiffY1 * (dX1 - dX3) + dDiffX1 * (dY1 - dY3)) / dTmp;
785
786 dTmp = -dDiffX2 * dDiffY1 + dDiffX1 * dDiffY2;
787 if (dTmp != 0)
788 dT = (dDiffX2 * (dY1 - dY3) - dDiffY2 * (dX1 - dX3)) / dTmp;
789
790 if (dS >= 0 && dS <= 1 && dT >= 0 && dT <= 1)
791 {
792 // Collision detected, calculate intersection co-ords
793 dXIntersect = dX1 + (dT * dDiffX1);
794 dYIntersect = dY1 + (dT * dDiffY1);
795
796 // And calc the average end-point co-ords
797 dXAvgEnd = (dX2 + dX4) / 2;
798 dYAvgEnd = (dY2 + dY4) / 2;
799
800 // Get the line segments at which intersection occurred
801 nProfile1LineSegment = i;
802 nProfile2LineSegment = j;
803
804 // LogStream << "\t" << "INTERSECTION dX2 = " << dX2 << " dX4 = " << dX4 << " dY2 = " << dY2 << " dY4 = " << dY4 << endl;
805 return true;
806 }
807 }
808 }
809
810 // No collision
811 return false;
812 }
813
814
815 /*==============================================================================================================================
816
817 Puts the coastline-normal profiles onto the raster grid, i.e. rasterizes multi-line vector objects onto the raster grid. Note that this doesn't work if the vector has already been interpolated to fit on the grid i.e. if distances between vector points are just one cell apart
818
819 ===============================================================================================================================*/
nPutAllProfilesOntoGrid(void)820 int CDelineation::nPutAllProfilesOntoGrid(void)
821 {
822 int nValidProfiles = 0;
823
824 // Do once for every coastline
825 for (int nCoast = 0; nCoast < static_cast<int>(m_VCoast.size()); nCoast++)
826 {
827 // How many profiles on this coast?
828 int nProfiles = m_VCoast[nCoast].nGetNumProfiles();
829 if (nProfiles == 0)
830 {
831 // This can happen if the coastline is very short, so just give a warning and carry on with the next coastline
832 LogStream << WARN << m_ulTimestep << ": coastline " << nCoast << " has no profiles" << endl;
833 continue;
834 }
835
836 // Now do this loop for every profile in the list
837 for (int nProfile = 0; nProfile < nProfiles; nProfile++)
838 {
839 CProfile* const pProfile = m_VCoast[nCoast].pGetProfile(nProfile);
840
841 // If this profile has a problem, then forget about it. Still do start- and end-of-coast profiles however
842 if (! pProfile->bOKIncStartAndEndOfCoast())
843 continue;
844
845 int nPoints = pProfile->nGetProfileSize();
846 if (nPoints < 2)
847 {
848 // Need at least two points in the profile, so this profile is invalid: mark it
849 pProfile->SetTooShort(true);
850 continue;
851 }
852
853 // OK, go for it: set up temporary vectors to hold the x-y coords (in grid CRS) of the cells which we will mark
854 vector<C2DIPoint> VCellsToMark;
855 vector<bool> bVShared;
856 bool
857 bTooShort = false,
858 bTruncated = false,
859 bHitCoast = false,
860 bHitAnotherProfile = false;
861
862 RasterizeProfile(nCoast, nProfile, &VCellsToMark, &bVShared, bTooShort, bTruncated, bHitCoast, bHitAnotherProfile);
863
864 //if ((! bTruncated) && (! bTooShort) && (! bHitCoast) && (! bHitAnotherProfile))
865 //if ( (! bTooShort) && (! bHitAnotherProfile))
866 if ( (! bTooShort))
867 {
868 // This profile is fine
869 nValidProfiles++;
870
871 for (unsigned int k = 0; k < VCellsToMark.size(); k++)
872 {
873 // So mark each cell in the raster grid
874 m_pRasterGrid->pGetCell(VCellsToMark[k].nGetX(), VCellsToMark[k].nGetY())->SetNormalProfile(nProfile);
875
876 // Store the raster-grid coordinates in the profile object
877 pProfile->AppendCellInProfile(VCellsToMark[k].nGetX(), VCellsToMark[k].nGetY());
878
879 // And also store the external coordinates in the profile object
880 pProfile->AppendCellInProfileExtCRS(dGridCentroidXToExtCRSX(VCellsToMark[k].nGetX()), dGridCentroidYToExtCRSY(VCellsToMark[k].nGetY()));
881
882 // Mark the shared (i.e. multi-line) parts of the profile (if any)
883 // if (bVShared[k])
884 // {
885 // pProfile->AppendPointShared(true);
886 // // LogStream << m_ulTimestep << ": profile " << j << " point " << k << " marked as shared" << endl;
887 // }
888 // else
889 // {
890 // pProfile->AppendPointShared(false);
891 // // LogStream << m_ulTimestep << ": profile " << nProfile << " point " << k << " marked as NOT shared" << endl;
892 // }
893 }
894 }
895 }
896 }
897
898 if (nValidProfiles == 0)
899 {
900 // Problem! No valid profiles. However, carry on
901 cerr << WARN << m_ulTimestep << ": no valid profiles" << endl;
902 }
903
904 return RTN_OK;
905 }
906
907
908 /*==============================================================================================================================
909
910 Given a pointer to a coastline-normal profile, returns an output vector of cells which are 'under' every line segment of the profile. If there is a problem with the profile (e.g. a rasterized cell is dry land or coast, or the profile has to be truncated) then we pass this back as an error code
911
912 ===============================================================================================================================*/
RasterizeProfile(int const nCoast,int const nProfile,vector<C2DIPoint> * pVIPointsOut,vector<bool> * pbVShared,bool & bTooShort,bool & bTruncated,bool & bHitCoast,bool & bHitAnotherProfile)913 void CDelineation::RasterizeProfile(int const nCoast, int const nProfile, vector<C2DIPoint>* pVIPointsOut, vector<bool>* pbVShared, bool& bTooShort, bool& bTruncated, bool& bHitCoast, bool& bHitAnotherProfile)
914 {
915 CProfile* const pProfile = m_VCoast[nCoast].pGetProfile(nProfile);
916
917 pVIPointsOut->clear();
918 int
919 nSeg = 0,
920 nSegments = pProfile->nGetNumLineSegments(),
921 nProfiles = m_VCoast[nCoast].nGetNumProfiles(); // TODO this is a bodge, needed if we hit a profile which belongs to a different coast object
922
923 for (nSeg = 0; nSeg < nSegments; nSeg++)
924 {
925 // Do once for every line segment
926 vector<C2DPoint> PtVSegment;
927 PtVSegment.push_back(*pProfile->pPtGetPointInProfile(nSeg));
928 PtVSegment.push_back(*pProfile->pPtGetPointInProfile(nSeg+1)); // This is OK
929
930 bool bShared = false;
931 if (pProfile->nGetNumCoincidentProfilesInLineSegment(nSeg) > 1)
932 bShared = true;
933
934 // The start point of the normal, must convert from the external CRS to grid CRS. If this is the first line segment of the profile, then the start point is the centroid of a coastline cell
935 double
936 dXStart = dExtCRSXToGridX(PtVSegment[0].dGetX()),
937 dYStart = dExtCRSYToGridY(PtVSegment[0].dGetY());
938
939 // The end point of the normal, again convert from the external CRS to grid CRS. Note too that it could be off the grid
940 double
941 dXEnd = dExtCRSXToGridX(PtVSegment[1].dGetX()),
942 dYEnd = dExtCRSYToGridY(PtVSegment[1].dGetY());
943
944 // 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
945 double
946 dXInc = dXEnd - dXStart,
947 dYInc = dYEnd - dYStart,
948 dLength = tMax(tAbs(dXInc), tAbs(dYInc));
949
950 dXInc /= dLength;
951 dYInc /= dLength;
952
953 double
954 dX = dXStart,
955 dY = dYStart;
956
957 // Process each interpolated point
958 for (int m = 0; m <= static_cast<int>(dRound(dLength)); m++)
959 {
960 int
961 nX = static_cast<int>(dX),
962 nY = static_cast<int>(dY);
963
964 // Do some checking of this interpolated point, but only if this is not a grid-edge profile (these profiles are always valid)
965 if ((! pProfile->bStartOfCoast()) && (! pProfile->bEndOfCoast()))
966 {
967 // Is the interpolated point within the raster grid?
968 if (! bIsWithinGrid(nX, nY))
969 {
970 // It isn't, so mark the too-short profile and quit
971 bTruncated = true;
972 pProfile->SetTruncated(true);
973
974 LogStream << m_ulTimestep << ": profile " << nProfile << " TRUNCATED at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
975
976 break;
977 }
978
979 // If this is the first line segment of the profile, then once we are clear of the coastline (say, when m > 1), check if this profile hits land at this interpolated point
980 // 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
981 if ((nSeg == 0) && (m > 1))
982 {
983 // 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)
984 if (m_pRasterGrid->pGetCell(nX, nY)->bIsCoastline() || (bIsWithinGrid(nX, nY+1) && m_pRasterGrid->pGetCell(nX, nY+1)->bIsCoastline()))
985 {
986 // We've hit a coastline so set a switch and mark the profile and truncate
987 bHitCoast = true;
988 pProfile->SetHitCoast(true);
989
990 LogStream << m_ulTimestep << ": profile " << nProfile << " HIT COAST at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
991
992 bTruncated = true;
993 pProfile->SetTruncated(true);
994 break;
995 }
996
997 // if (! m_pRasterGrid->pGetCell(nX, nY)->bIsInContiguousSea())
998 // {
999 // // We've hit dry land so set a switch and mark the profile, however keep going
1000 // bHitLand = true;
1001 // pProfile->SetHitLand(true);
1002 //
1003 // LogStream << m_ulTimestep << ": profile " << nProfile << " HIT LAND at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation = " << m_pRasterGrid->pGetCell(nX, nY)->dGetSedimentTopElev() << ", SWL = " << m_dStillWaterLevel << endl;
1004 //
1005 // LogStream << "On [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, sea depth = " << m_pRasterGrid->pGetCell(nX, nY)->dGetSeaDepth() << ", bIsInContiguousSea = " << (m_pRasterGrid->pGetCell(nX, nY)->bIsInContiguousSea() ? "true" : "false") << ", landform = " << (m_pRasterGrid->pGetCell(nX, nY)->bIsInContiguousSea() ? "sea" : "not sea") << endl;
1006 //
1007 // LogStream << "On [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}, elevation of consolidated sediment = " << m_pRasterGrid->pGetCell(nX, nY)->dGetConsSedTopForLayer(m_pRasterGrid->pGetCell(nX, nY)->nGetTopNonZeroLayer()) << ", total cliff collapse = " << m_pRasterGrid->pGetCell(nX, nY)->dGetTotCliffCollapse() << ", total beach deposition = " << m_pRasterGrid->pGetCell(nX, nY)->dGetTotBeachDeposition() << endl;
1008 // }
1009 }
1010
1011 // Check to see if we hit another profile which is not a coincident normal to this normal
1012 static int nLastProfileChecked = -1;
1013 if ((nProfile != nLastProfileChecked) && m_pRasterGrid->pGetCell(nX, nY)->bIsNormalProfile())
1014 {
1015 // For the first time for this profile, 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
1016 int nHitProfile = m_pRasterGrid->pGetCell(nX, nY)->nGetNormalProfile();
1017
1018 // TODO Bodge in case we hit a profile which belongs to a different coast
1019 if (nHitProfile > nProfiles-1)
1020 {
1021 bHitAnotherProfile = true;
1022 pProfile->SetHitAnotherProfile(true);
1023
1024 LogStream << m_ulTimestep << ": profile " << nProfile << " hit another profile A (" << nHitProfile << ") at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1025 }
1026 else
1027 {
1028 // Only set the flag if this isn't a coincident normal to one or other of the profiles
1029 if ((! pProfile->bFindProfileInCoincidentProfiles(nHitProfile)) && (! m_VCoast[nCoast].pGetProfile(nHitProfile)->bFindProfileInCoincidentProfiles(nProfile)))
1030 {
1031 bHitAnotherProfile = true;
1032 pProfile->SetHitAnotherProfile(true);
1033
1034 LogStream << m_ulTimestep << ": profile " << nProfile << " hit another profile B (" << nHitProfile << ") at [" << nX << "][" << nY << "] = {" << dGridCentroidXToExtCRSX(nX) << ", " << dGridCentroidYToExtCRSY(nY) << "}" << endl;
1035 }
1036
1037 nLastProfileChecked = nProfile;
1038 }
1039 }
1040 }
1041
1042 // Append this point to the output vector
1043 pVIPointsOut->push_back(C2DIPoint(nX, nY)); // Is in raster-grid co-ordinates
1044 pbVShared->push_back(bShared);
1045
1046 // And increment for next time
1047 dX += dXInc;
1048 dY += dYInc;
1049 }
1050
1051 if (bTruncated)
1052 break;
1053 }
1054
1055 if (bTruncated)
1056 {
1057 if (nSeg < (nSegments-1))
1058 // We are truncating the profile, so remove any line segments after this one
1059 pProfile->TruncateLineSegments(nSeg);
1060
1061 // Shorten the vector input
1062 int
1063 nOutSize = pVIPointsOut->size(),
1064 nLastX = pVIPointsOut->at(nOutSize-1).nGetX(),
1065 nLastY = pVIPointsOut->at(nOutSize-1).nGetY();
1066
1067 pProfile->pPtGetPointInProfile(nSeg+1)->SetX(dGridCentroidXToExtCRSX(nLastX));
1068 pProfile->pPtGetPointInProfile(nSeg+1)->SetY(dGridCentroidYToExtCRSY(nLastY));
1069
1070 }
1071
1072 if (pVIPointsOut->size() < 3)
1073 {
1074 // For shore platform coastline-normal profiles, we cannot have very short normal profiles with less than 3 cells, since we cannot calculate along-profile slope properly for such short profiles
1075 bTooShort = true;
1076 pProfile->SetTooShort(true);
1077
1078 LogStream << m_ulTimestep << ": profile " << nProfile << " is TOO SHORT" << endl;
1079 }
1080 }
1081
1082
1083 /*==============================================================================================================================
1084
1085 Merges two profiles which intersect at their final (most seaward) line segments, seaward of their point of intersection
1086
1087 ===============================================================================================================================*/
MergeProfilesAtFinalLineSegments(int const nCoast,int const nFirstProfile,int const nSecondProfile,int const nFirstProfileLineSegments,int const nSecondProfileLineSegments,double const dIntersectX,double const dIntersectY,double const dAvgEndX,double const dAvgEndY)1088 void CDelineation::MergeProfilesAtFinalLineSegments(int const nCoast, int const nFirstProfile, int const nSecondProfile, int const nFirstProfileLineSegments, int const nSecondProfileLineSegments, double const dIntersectX, double const dIntersectY, double const dAvgEndX, double const dAvgEndY)
1089 {
1090 // 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
1091 int nCombinedLastSeg = 0;
1092 vector<pair<int, int> >
1093 prVCombinedProfilesCoincidentProfilesLastSeg;
1094 CProfile* pFirstProfile = m_VCoast[nCoast].pGetProfile(nFirstProfile);
1095 CProfile* pSecondProfile = m_VCoast[nCoast].pGetProfile(nSecondProfile);
1096
1097 for (unsigned int n = 0; n < pFirstProfile->pprVGetCoincidentProfilesForLineSegment(nFirstProfileLineSegments-1)->size(); n++)
1098 {
1099 pair<int, int> prTmp;
1100 prTmp.first = pFirstProfile->pprVGetCoincidentProfilesForLineSegment(nFirstProfileLineSegments-1)->at(n).first;
1101 prTmp.second = pFirstProfile->pprVGetCoincidentProfilesForLineSegment(nFirstProfileLineSegments-1)->at(n).second;
1102
1103 bool bFound = false;
1104 for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++)
1105 {
1106 if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first)
1107 {
1108 bFound = true;
1109 break;
1110 }
1111 }
1112
1113 if (! bFound)
1114 {
1115 prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp);
1116 nCombinedLastSeg++;
1117 }
1118 }
1119
1120 for (unsigned int n = 0; n < pSecondProfile->pprVGetCoincidentProfilesForLineSegment(nSecondProfileLineSegments-1)->size(); n++)
1121 {
1122 pair<int, int> prTmp;
1123 prTmp.first = pSecondProfile->pprVGetCoincidentProfilesForLineSegment(nSecondProfileLineSegments-1)->at(n).first;
1124 prTmp.second = pSecondProfile->pprVGetCoincidentProfilesForLineSegment(nSecondProfileLineSegments-1)->at(n).second;
1125
1126 bool bFound = false;
1127 for (unsigned int m = 0; m < prVCombinedProfilesCoincidentProfilesLastSeg.size(); m++)
1128 {
1129 if (prVCombinedProfilesCoincidentProfilesLastSeg[m].first == prTmp.first)
1130 {
1131 bFound = true;
1132 break;
1133 }
1134 }
1135
1136 if (! bFound)
1137 {
1138 prVCombinedProfilesCoincidentProfilesLastSeg.push_back(prTmp);
1139 nCombinedLastSeg++;
1140 }
1141 }
1142
1143 // Increment the number of each line segment
1144 for (int m = 0; m < nCombinedLastSeg; m++)
1145 prVCombinedProfilesCoincidentProfilesLastSeg[m].second++;
1146
1147 vector<pair<int, int> >
1148 prVFirstProfileCoincidentProfilesLastSeg = *pFirstProfile->pprVGetCoincidentProfilesForLineSegment(nFirstProfileLineSegments-1),
1149 prVSecondProfileCoincidentProfilesLastSeg = *pSecondProfile->pprVGetCoincidentProfilesForLineSegment(nSecondProfileLineSegments-1);
1150 int
1151 nNumFirstProfileCoincidentProfilesLastSeg = prVFirstProfileCoincidentProfilesLastSeg.size(),
1152 nNumSecondProfileCoincidentProfilesLastSeg = prVSecondProfileCoincidentProfilesLastSeg.size();
1153
1154 // LogStream << m_ulTimestep << ": 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;
1155
1156 // Truncate the first profile, and all co-incident profiles, at the point of intersection
1157 for (int n = 0; n < nNumFirstProfileCoincidentProfilesLastSeg; n++)
1158 {
1159 int nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[n].first;
1160 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1161 int nProfileLength = pThisProfile->nGetProfileSize();
1162
1163 // NOTE: 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
1164 pThisProfile->SetPointInProfile(nProfileLength-1, dIntersectX, dIntersectY);
1165 }
1166
1167 // Truncate the second profile, and all co-incident profiles, at the point of intersection
1168 for (int n = 0; n < nNumSecondProfileCoincidentProfilesLastSeg; n++)
1169 {
1170 int nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[n].first;
1171 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1172 int nProfileLength = pThisProfile->nGetProfileSize();
1173
1174 // NOTE: 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
1175 pThisProfile->SetPointInProfile(nProfileLength-1, dIntersectX, dIntersectY);
1176 }
1177
1178 // Append a new straight line segment to the existing line segment(s) of the first profile, and to all co-incident profiles
1179 for (int nThisLineSeg = 0; nThisLineSeg < nNumFirstProfileCoincidentProfilesLastSeg; nThisLineSeg++)
1180 {
1181 int nThisProfile = prVFirstProfileCoincidentProfilesLastSeg[nThisLineSeg].first;
1182 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1183
1184 // Update this profile
1185 pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY);
1186
1187 // Append details of the combined profiles
1188 pThisProfile->AppendLineSegment();
1189 for (int m = 0; m < nCombinedLastSeg; m++)
1190 pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]);
1191 }
1192
1193 // Append a new straight line segment to the existing line segment(s) of the second profile, and to all co-incident profiles
1194 for (int nThisLineSeg = 0; nThisLineSeg < nNumSecondProfileCoincidentProfilesLastSeg; nThisLineSeg++)
1195 {
1196 int nThisProfile = prVSecondProfileCoincidentProfilesLastSeg[nThisLineSeg].first;
1197 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1198
1199 // Update this profile
1200 pThisProfile->AppendPointInProfile(dAvgEndX, dAvgEndY);
1201
1202 // Append details of the combined profiles
1203 pThisProfile->AppendLineSegment();
1204 for (int m = 0; m < nCombinedLastSeg; m++)
1205 pThisProfile->AppendCoincidentProfileToLineSegments(prVCombinedProfilesCoincidentProfilesLastSeg[m]);
1206 }
1207
1208
1209 // START: FOR CHECKING PURPOSES ****************************************************************
1210 // int nFirstProfileLineSeg= pFirstProfile->nGetNumLineSegments();
1211 // int nSecondProfileLineSeg = pSecondProfile->nGetNumLineSegments();
1212 //
1213 // LogStream << "\tProfile {" << nFirstProfile << "} now has " << nFirstProfileLineSeg << " line segments" << endl;
1214 // for (int m = 0; m < nFirstProfileLineSeg; m++)
1215 // {
1216 // vector<pair<int, int> > prVCoincidentProfiles = *pFirstProfile->pprVGetCoincidentProfilesForLineSegment(m);
1217 // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nFirstProfile << "} are {";
1218 // for (unsigned int nn = 0; nn < prVCoincidentProfiles.size(); nn++)
1219 // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] ";
1220 // LogStream << " }" << endl;
1221 // }
1222 // LogStream << "\tProfile {" << nSecondProfile << "} now has " << nSecondProfileLineSeg << " line segments" << endl;
1223 // for (int m = 0; m < nSecondProfileLineSeg; m++)
1224 // {
1225 // vector<pair<int, int> > prVCoincidentProfiles = *pSecondProfile->pprVGetCoincidentProfilesForLineSegment(m);
1226 // LogStream << "\tCo-incident profiles and line segments for line segment " << m << " of profile {" << nSecondProfile << "} are {";
1227 // for (unsigned int nn = 0; nn < prVCoincidentProfiles.size(); nn++)
1228 // LogStream << " " << prVCoincidentProfiles[nn].first << "[" << prVCoincidentProfiles[nn].second << "] ";
1229 // LogStream << " }" << endl;
1230 // }
1231 // END: FOR CHECKING PURPOSES ******************************************************************
1232 }
1233
1234
1235 /*==============================================================================================================================
1236
1237 Truncates one intersecting profile at the point of intersection, and retains the other profile
1238
1239 ===============================================================================================================================*/
TruncateOneProfileRetainOtherProfile(int const nCoast,int const nProfileToTruncate,int const nProfileToRetain,double const dIntersectX,double const dIntersectY,int const nProfileToTruncateIntersectLineSeg,int const nProfileToRetainIntersectLineSeg,bool const bAlreadyPresent)1240 void CDelineation::TruncateOneProfileRetainOtherProfile(int const nCoast, int const nProfileToTruncate, int const nProfileToRetain, double const dIntersectX, double const dIntersectY, int const nProfileToTruncateIntersectLineSeg, int const nProfileToRetainIntersectLineSeg, bool const bAlreadyPresent)
1241 {
1242 // 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
1243 int nRet = nInsertPointIntoProfilesIfNeededThenUpdate(nCoast, nProfileToRetain, dIntersectX, dIntersectY, nProfileToRetainIntersectLineSeg, nProfileToTruncate, nProfileToTruncateIntersectLineSeg, bAlreadyPresent);
1244 if (nRet != RTN_OK)
1245 {
1246 LogStream << m_ulTimestep << ": error in nInsertPointIntoProfilesIfNeededThenUpdate()" << endl;
1247 return;
1248 }
1249
1250 // 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)
1251 CProfile* pProfileToRetain = m_VCoast[nCoast].pGetProfile(nProfileToRetain);
1252 vector<C2DPoint> PtVProfileLastPart;
1253 vector<vector<pair<int, int> > > prVLineSegLastPart;
1254 if (bAlreadyPresent)
1255 {
1256 PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg);
1257 prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg);
1258 }
1259 else
1260 {
1261 PtVProfileLastPart = pProfileToRetain->PtVGetThisPointAndAllAfter(nProfileToRetainIntersectLineSeg+1);
1262 prVLineSegLastPart = pProfileToRetain->prVVGetAllLineSegAfter(nProfileToRetainIntersectLineSeg+1);
1263 }
1264
1265 // assert(PtVProfileLastPart.size() > 1);
1266 // assert(prVLineSegLastPart.size() > 0);
1267
1268 // 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)
1269 TruncateProfileAndAppendNew(nCoast, nProfileToTruncate, nProfileToTruncateIntersectLineSeg, &PtVProfileLastPart, &prVLineSegLastPart);
1270
1271 // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetProfileSize() > 1);
1272 // assert(pProfileToRetain->nGetNumLineSegments() > 0);
1273 // assert(m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->nGetNumLineSegments() > 0);
1274 }
1275
1276
1277 /*===============================================================================================================================
1278
1279 Inserts an intersection point into the profile that is to be retained, if that point is not already present in the profile, then does the same for all co-incident profiles. Finally adds the numbers of the to-truncate profile (and all its coincident profiles) to the seaward line segments of the to-retain profile and all its coincident profiles
1280
1281 ===============================================================================================================================*/
nInsertPointIntoProfilesIfNeededThenUpdate(int const nCoast,int const nMainProfile,double const dIntersectX,double const dIntersectY,int const nMainProfileIntersectLineSeg,int const nProfileToTruncate,int const nProfileToTruncateIntersectLineSeg,bool const bAlreadyPresent)1282 int CDelineation::nInsertPointIntoProfilesIfNeededThenUpdate(int const nCoast, int const nMainProfile, double const dIntersectX, double const dIntersectY, int const nMainProfileIntersectLineSeg, int const nProfileToTruncate, int const nProfileToTruncateIntersectLineSeg, bool const bAlreadyPresent)
1283 {
1284 // START: FOR CHECKING PURPOSES ****************************************************************
1285 // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurred
1286 // vector<pair<int, int> > prVRetainCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
1287 // int nNumRetainCoincidentCHECK1 = prVRetainCoincidentProfilesCHECK1.size();
1288 // for (int nn = 0; nn < nNumRetainCoincidentCHECK1; nn++)
1289 // {
1290 // int nThisProfile = prVRetainCoincidentProfilesCHECK1[nn].first;
1291 // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ";
1292 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1293 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1294 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are ";
1295 // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++)
1296 // {
1297 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetCoincidentProfilesForLineSegment(mm);
1298 // LogStream << "{ ";
1299 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++)
1300 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1301 // LogStream << "} ";
1302 // }
1303 // LogStream << endl;
1304 //
1305 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1306 // {
1307 // C2DPoint
1308 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1309 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1310 //
1311 // if (Pt1 == Pt2)
1312 // LogStream << m_ulTimestep << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1313 // }
1314 // }
1315 // // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred
1316 // vector<pair<int, int> > prVTruncateCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->pprVGetCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg);
1317 // int nNumTruncateCoincidentCHECK1 = prVTruncateCoincidentProfilesCHECK1.size();
1318 // for (int nn = 0; nn < nNumTruncateCoincidentCHECK1; nn++)
1319 // {
1320 // int nThisProfile = prVTruncateCoincidentProfilesCHECK1[nn].first;
1321 // LogStream << "\tBEFORE nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nProfileToTruncate ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ";
1322 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1323 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1324 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are ";
1325 // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++)
1326 // {
1327 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetCoincidentProfilesForLineSegment(mm);
1328 // LogStream << "{ ";
1329 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++)
1330 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1331 // LogStream << "} ";
1332 // }
1333 // LogStream << endl;
1334 //
1335 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1336 // {
1337 // C2DPoint
1338 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1339 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1340 //
1341 // if (Pt1 == Pt2)
1342 // LogStream << m_ulTimestep << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1343 // }
1344 // }
1345 // END: FOR CHECKING PURPOSES ******************************************************************
1346
1347 // Get the index numbers of all coincident profiles for the 'main' to-retain profile for the line segment in which intersection occurs
1348 vector<pair<int, int> > prVCoincidentProfiles = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
1349 int nNumCoincident = prVCoincidentProfiles.size();
1350 vector<int> nLineSegAfterIntersect(nNumCoincident, -1); // The line segment after the point of intersection, for each co-incident profile
1351
1352 // Do this for the main profile and all profiles which are co-incident for this line segment
1353 for (int nn = 0; nn < nNumCoincident; nn++)
1354 {
1355 int
1356 nThisProfile = prVCoincidentProfiles[nn].first, // The number of this profile
1357 nThisLineSeg = prVCoincidentProfiles[nn].second; // The line segment of this profile
1358 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1359
1360 // Is the intersection point already present in the to-retain profile?
1361 if (! bAlreadyPresent)
1362 {
1363 // It is not already present, so insert it and also update the associated multi-line
1364 if (! pThisProfile->bInsertIntersection(dIntersectX, dIntersectY, nThisLineSeg))
1365 {
1366 // Error
1367 LogStream << WARN << m_ulTimestep << ": cannot insert a line segment after the final line segment (" << nThisLineSeg << ") for " << (nThisProfile == nMainProfile ? "main" : "co-incident") << " profile (" << nThisProfile << "), abandoning" << endl;
1368
1369 return RTN_ERR_CANNOT_INSERT_POINT;
1370 }
1371
1372 // LogStream << "\tIntersection point NOT already in " << (nThisProfile == nMainProfile ? "main" : "co-incident") << " profile {" << nThisProfile << "}, inserted it as point " << nThisLineSeg+1 << endl;
1373 }
1374
1375 // Get the line segment after intersection
1376 nLineSegAfterIntersect[nn] = nThisLineSeg+1;
1377 }
1378
1379 // for (int nn = 0; nn < nNumCoincident; nn++)
1380 // LogStream << "\tFor profile {" << prVCoincidentProfiles[nn].first << "} line segment [" << nLineSegAfterIntersect[nn] << "] is immediately after the intersection point" << endl;
1381
1382 // Get the coincident profiles for the to-truncate profile, at the line segment where intersection occurs
1383 vector<pair<int, int> > prVToTruncateCoincidentProfiles = *m_VCoast[nCoast].pGetProfile(nProfileToTruncate)->pprVGetCoincidentProfilesForLineSegment(nProfileToTruncateIntersectLineSeg);
1384 int nNumToTruncateCoincident = prVToTruncateCoincidentProfiles.size();
1385
1386 // 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
1387 for (int nn = 0; nn < nNumCoincident; nn++)
1388 {
1389 int nThisProfile = prVCoincidentProfiles[nn].first; // The number of this profile
1390 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1391
1392 // Get the number of line segments for this to-retain profile (will have just increased, if we just inserted a point)
1393 int nNumLineSegs = pThisProfile->nGetNumLineSegments();
1394
1395 // Do for all line segments seaward of the point of intersection
1396 for (int nLineSeg = nLineSegAfterIntersect[nn], nIncr = 0; nLineSeg < nNumLineSegs; nLineSeg++, nIncr++)
1397 {
1398 // LogStream << "\tnThisProfile = " << nThisProfile << " nThisLineSeg = " << nThisLineSeg << " nLineSeg = " << nLineSeg << " nNumLineSegs = " << nNumLineSegs << endl;
1399
1400 // Have no idea how this can happen, but check for the moment
1401 // if (nThisProfile == nProfileToTruncate)
1402 // {
1403 // LogStream << "\t*** ERROR nThisProfile = " << nThisProfile << " nProfileToTruncate = " << nProfileToTruncate << ", ignoring" << endl;
1404 // continue;
1405 // }
1406
1407 // Add the number of the to-truncate profile, and all its coincident profiles, to this line segment
1408 for (int m = 0; m < nNumToTruncateCoincident; m++)
1409 {
1410 int
1411 nProfileToAdd = prVToTruncateCoincidentProfiles[m].first,
1412 nProfileToAddLineSeg = prVToTruncateCoincidentProfiles[m].second;
1413
1414 // LogStream << "\tAdding " << (nProfileToAdd == nProfileToTruncate ? "main" : "co-incident") << " truncate-profile number {" << nProfileToAdd << "}, line segment [" << nProfileToAddLineSeg + nIncr << "] to line segment " << nLineSeg << " of " << (nThisProfile == nMainProfile ? "main" : "co-incident") << " to-retain profile {" << nThisProfile << "}" << endl;
1415
1416 pThisProfile->AddCoincidentProfileToExistingLineSegment(nLineSeg, nProfileToAdd, nProfileToAddLineSeg + nIncr);
1417 }
1418 }
1419 }
1420
1421
1422 // START: FOR CHECKING PURPOSES ****************************************************************
1423 // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred
1424 // vector<pair<int, int> > prVCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
1425 // int nNumCoincidentCHECK2 = prVCoincidentProfilesCHECK2.size();
1426 // for (int nn = 0; nn < nNumCoincidentCHECK2; nn++)
1427 // {
1428 // int nThisProfile = prVCoincidentProfilesCHECK2[nn].first;
1429 // LogStream << "\tAFTER nInsertPointIntoProfilesIfNeededThenUpdate(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-retain profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points ";
1430 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1431 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1432 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles and their line segments are ";
1433 // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++)
1434 // {
1435 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetCoincidentProfilesForLineSegment(nLineSeg);
1436 // LogStream << "{ ";
1437 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++)
1438 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1439 // LogStream << "} ";
1440 // }
1441 // LogStream << endl;
1442 //
1443 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1444 // {
1445 // C2DPoint
1446 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1447 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1448 //
1449 // if (Pt1 == Pt2)
1450 // LogStream << m_ulTimestep << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1451 // }
1452 // }
1453 // END: FOR CHECKING PURPOSES ******************************************************************
1454
1455 return RTN_OK;
1456 }
1457
1458
1459 /*===============================================================================================================================
1460
1461 Truncate a profile at the point of intersection, and do the same for all its co-incident profiles
1462
1463 ===============================================================================================================================*/
TruncateProfileAndAppendNew(int const nCoast,int const nMainProfile,int const nMainProfileIntersectLineSeg,vector<C2DPoint> * const pPtVProfileLastPart,vector<vector<pair<int,int>>> * const pprVLineSegLastPart)1464 void CDelineation::TruncateProfileAndAppendNew(int const nCoast, int const nMainProfile, int const nMainProfileIntersectLineSeg, vector<C2DPoint>* const pPtVProfileLastPart, vector<vector<pair<int, int> > >* const pprVLineSegLastPart)
1465 {
1466 // START: FOR CHECKING PURPOSES ****************************************************************
1467 // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurred
1468 // vector<pair<int, int> > prVCoincidentProfilesCHECK1 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
1469 // int nNumCoincidentCHECK1 = prVCoincidentProfilesCHECK1.size();
1470 //
1471 // LogStream << "\tTruncating profile {" << nMainProfile << "}, intersection is at [" << dIntersectX << ", " << dIntersectY << "] in line segment " << nMainProfileIntersectLineSeg << endl;
1472 // for (int nn = 0; nn < nNumCoincidentCHECK1; nn++)
1473 // {
1474 // int nThisProfile = prVCoincidentProfilesCHECK1[nn].first;
1475 // LogStream << "\tBEFORE TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points (";
1476 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1477 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1478 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are ";
1479 // for (int nLineSeg = 0; nLineSeg < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); nLineSeg++)
1480 // {
1481 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetCoincidentProfilesForLineSegment(nLineSeg);
1482 // LogStream << "{ ";
1483 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(nLineSeg); nn++)
1484 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1485 // LogStream << "} ";
1486 // }
1487 // LogStream << endl;
1488 //
1489 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1490 // {
1491 // C2DPoint
1492 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1493 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1494 //
1495 // if (Pt1 == Pt2)
1496 // LogStream << m_ulTimestep << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1497 // }
1498 // }
1499 // LogStream << "\tPart-profile to append is ";
1500 // for (unsigned int mm = 0; mm < pPtVProfileLastPart->size(); mm++)
1501 // LogStream << "[" << pPtVProfileLastPart->at(mm).dGetX() << ", " << pPtVProfileLastPart->at(mm).dGetY() << "] ";
1502 // LogStream << endl;
1503 // LogStream << "\tPart line-segment to append is ";
1504 // for (unsigned int mm = 0; mm < pprVLineSegLastPart->size(); mm++)
1505 // {
1506 // vector<pair<int, int> > prVTmp = pprVLineSegLastPart->at(mm);
1507 // LogStream << "{ ";
1508 // for (unsigned int nn = 0; nn < prVTmp.size(); nn++)
1509 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1510 // LogStream << "} ";
1511 // }
1512 // LogStream << endl;
1513 // END: FOR CHECKING PURPOSES ******************************************************************
1514
1515
1516 // Get the index numbers of all coincident profiles for the 'main' profile for the line segment in which intersection occurs
1517 vector<pair<int, int> > prVCoincidentProfiles = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
1518 int nNumCoincident = prVCoincidentProfiles.size();
1519
1520 for (int nn = 0; nn < nNumCoincident; nn++)
1521 {
1522 // Do this for the main to-truncate profile, and do the same for all its co-incident profiles
1523 int
1524 nThisProfile = prVCoincidentProfiles[nn].first,
1525 nThisProfileLineSeg = prVCoincidentProfiles[nn].second;
1526 CProfile* pThisProfile = m_VCoast[nCoast].pGetProfile(nThisProfile);
1527
1528 // if (nThisProfile == nMainProfile)
1529 // assert(nThisProfileLineSeg == nMainProfileIntersectLineSeg);
1530
1531 // Truncate the profile
1532 // LogStream << "\tTruncating " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} at line segment " << nThisProfileLineSeg+1 << endl;
1533 pThisProfile->TruncateProfile(nThisProfileLineSeg+1);
1534
1535 // Reduce the number of line segments for this profile
1536 pThisProfile->TruncateLineSegments(nThisProfileLineSeg+1);
1537
1538 // Append the profile points from the last part of the retain-profile
1539 for (unsigned int mm = 0; mm < pPtVProfileLastPart->size(); mm++)
1540 {
1541 C2DPoint Pt = pPtVProfileLastPart->at(mm);
1542 pThisProfile->AppendPointInProfile(&Pt);
1543 }
1544
1545 // Append the line segments, and their co-incident profile numbers, from the last part of the retain-profile
1546 for (unsigned int mm = 0; mm < pprVLineSegLastPart->size(); mm++)
1547 {
1548 vector<pair<int, int> > prVTmp = pprVLineSegLastPart->at(mm);
1549
1550 pThisProfile->AppendLineSegment(&prVTmp);
1551 }
1552
1553 // Fix the line seg numbers for this profile
1554 vector<int>
1555 nVProf,
1556 nVProfsLineSeg;
1557 for (int nSeg = 0; nSeg < pThisProfile->nGetNumLineSegments(); nSeg++)
1558 {
1559 for (int nCoinc = 0; nCoinc < pThisProfile->nGetNumCoincidentProfilesInLineSegment(nSeg); nCoinc++)
1560 {
1561 int
1562 nProf = pThisProfile->nGetProf(nSeg, nCoinc),
1563 nProfsLineSeg = pThisProfile->nGetProfsLineSeg(nSeg, nCoinc);
1564
1565 vector<int>::iterator it = std::find(nVProf.begin(), nVProf.end(), nProf);
1566 if (it == nVProf.end())
1567 {
1568 // Not found
1569 nVProf.push_back(nProf);
1570 nVProfsLineSeg.push_back(nProfsLineSeg);
1571 }
1572 else
1573 {
1574 // Found
1575 int nPos = it - nVProf.begin();
1576 int nNewProfsLineSeg = nVProfsLineSeg[nPos];
1577 nNewProfsLineSeg++;
1578
1579 nVProfsLineSeg[nPos] = nNewProfsLineSeg;
1580 pThisProfile->SetProfsLineSeg(nSeg, nCoinc, nNewProfsLineSeg);
1581 }
1582 }
1583 }
1584
1585 // assert(pThisProfile->nGetProfileSize() > 1);
1586 }
1587
1588
1589 // START: FOR CHECKING PURPOSES ****************************************************************
1590 // Get the index numbers of all coincident profiles for the 'main' to-truncate profile for the line segment in which intersection occurred
1591 // vector<pair<int, int> > prVToTruncateCoincidentProfilesCHECK2 = *m_VCoast[nCoast].pGetProfile(nMainProfile)->pprVGetCoincidentProfilesForLineSegment(nMainProfileIntersectLineSeg);
1592 // int nNumToTruncateCoincidentCHECK2 = prVToTruncateCoincidentProfilesCHECK2.size();
1593 // for (int nn = 0; nn < nNumToTruncateCoincidentCHECK2; nn++)
1594 // {
1595 // int nThisProfile = prVToTruncateCoincidentProfilesCHECK2[nn].first;
1596 // LogStream << "\tAFTER TruncateProfileAndAppendNew(): " << (nThisProfile == nMainProfile ? "MAIN" : "COINCIDENT") << " to-truncate profile {" << nThisProfile << "} has " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize() << " points (";
1597 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize(); nn++)
1598 // LogStream << "[" << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetX() << ", " << m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nn)->dGetY() << "] ";
1599 // LogStream << "), and " << m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments() << " line segments, co-incident profiles are ";
1600 // for (int mm = 0; mm < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumLineSegments(); mm++)
1601 // {
1602 // vector<pair<int, int> > prVTmp = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pprVGetCoincidentProfilesForLineSegment(mm);
1603 // LogStream << "{ ";
1604 // for (int nn = 0; nn < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetNumCoincidentProfilesInLineSegment(mm); nn++)
1605 // LogStream << prVTmp[nn].first << "[" << prVTmp[nn].second << "] ";
1606 // LogStream << "} ";
1607 // }
1608 // LogStream << endl;
1609 //
1610 // for (int nPoint = 0; nPoint < m_VCoast[nCoast].pGetProfile(nThisProfile)->nGetProfileSize()-1; nPoint++)
1611 // {
1612 // C2DPoint
1613 // Pt1 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint),
1614 // Pt2 = *m_VCoast[nCoast].pGetProfile(nThisProfile)->pPtGetPointInProfile(nPoint+1);
1615 //
1616 // if (Pt1 == Pt2)
1617 // LogStream << m_ulTimestep << ": IDENTICAL POINTS before changes, in profile {" << nThisProfile << "} points = " << nPoint << " and " << nPoint+1 << endl;
1618 // }
1619 // }
1620 // END: FOR CHECKING PURPOSES ******************************************************************
1621 }
1622