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