|   |  | 1 |  | using System; | 
|   |  | 2 |  | using System.Collections.Generic; | 
|   |  | 3 |  | using Itinero.Geo.Elevation; | 
|   |  | 4 |  |  | 
|   |  | 5 |  | namespace Itinero.Geo; | 
|   |  | 6 |  |  | 
|   |  | 7 |  | /// <summary> | 
|   |  | 8 |  | /// Contains extension methods to work with coordinates, lines, bounding boxes and basic spatial operations. | 
|   |  | 9 |  | /// </summary> | 
|   |  | 10 |  | public static class GeoExtensions | 
|   |  | 11 |  | { | 
|   |  | 12 |  |     /// <summary> | 
|   |  | 13 |  |     /// Returns an estimate of the distance between the two given coordinates. | 
|   |  | 14 |  |     /// </summary> | 
|   |  | 15 |  |     /// <param name="coordinate1">The first coordinate.</param> | 
|   |  | 16 |  |     /// <param name="coordinate2">The second coordinate.</param> | 
|   |  | 17 |  |     /// <remarks>Accuracy decreases with distance.</remarks> | 
|   |  | 18 |  |     public static double DistanceEstimateInMeter(this (double longitude, double latitude, float? e) coordinate1, | 
|   |  | 19 |  |         (double longitude, double latitude, float? e) coordinate2) | 
|   | 1149 | 20 |  |     { | 
|   | 1149 | 21 |  |         var lat1Rad = coordinate1.latitude / 180d * Math.PI; | 
|   | 1149 | 22 |  |         var lon1Rad = coordinate1.longitude / 180d * Math.PI; | 
|   | 1149 | 23 |  |         var lat2Rad = coordinate2.latitude / 180d * Math.PI; | 
|   | 1149 | 24 |  |         var lon2Rad = coordinate2.longitude / 180d * Math.PI; | 
|   |  | 25 |  |  | 
|   | 1149 | 26 |  |         var x = (lon2Rad - lon1Rad) * Math.Cos((lat1Rad + lat2Rad) / 2.0); | 
|   | 1149 | 27 |  |         var y = lat2Rad - lat1Rad; | 
|   |  | 28 |  |  | 
|   | 1149 | 29 |  |         var m = Math.Sqrt((x * x) + (y * y)) * Constants.RadiusOfEarth; | 
|   |  | 30 |  |  | 
|   | 1149 | 31 |  |         return m; | 
|   | 1149 | 32 |  |     } | 
|   |  | 33 |  |  | 
|   |  | 34 |  |     internal static double DistanceEstimateInMeterShape( | 
|   |  | 35 |  |         this (double longitude, double latitude, float? e) coordinate1, | 
|   |  | 36 |  |         (double longitude, double latitude, float? e) coordinate2, | 
|   |  | 37 |  |         IEnumerable<(double longitude, double latitude, float? e)>? shape = null) | 
|   | 42 | 38 |  |     { | 
|   | 42 | 39 |  |         if (shape == null) | 
|   | 31 | 40 |  |         { | 
|   | 31 | 41 |  |             return coordinate1.DistanceEstimateInMeter(coordinate2); | 
|   |  | 42 |  |         } | 
|   |  | 43 |  |  | 
|   | 11 | 44 |  |         var distance = 0.0; | 
|   |  | 45 |  |  | 
|   | 11 | 46 |  |         using var shapeEnumerator = shape.GetEnumerator(); | 
|   | 11 | 47 |  |         var previous = coordinate1; | 
|   |  | 48 |  |  | 
|   | 15 | 49 |  |         while (shapeEnumerator.MoveNext()) | 
|   | 4 | 50 |  |         { | 
|   | 4 | 51 |  |             var current = shapeEnumerator.Current; | 
|   | 4 | 52 |  |             distance += previous.DistanceEstimateInMeter(current); | 
|   | 4 | 53 |  |             previous = current; | 
|   | 4 | 54 |  |         } | 
|   |  | 55 |  |  | 
|   | 11 | 56 |  |         distance += previous.DistanceEstimateInMeter(coordinate2); | 
|   |  | 57 |  |  | 
|   | 11 | 58 |  |         return distance; | 
|   | 42 | 59 |  |     } | 
|   |  | 60 |  |  | 
|   |  | 61 |  |     /// <summary> | 
|   |  | 62 |  |     /// Returns an estimate of the length of the given linestring. | 
|   |  | 63 |  |     /// </summary> | 
|   |  | 64 |  |     /// <param name="lineString">The linestring.</param> | 
|   |  | 65 |  |     /// <remarks>Accuracy decreases with distance.</remarks> | 
|   |  | 66 |  |     public static double DistanceEstimateInMeter( | 
|   |  | 67 |  |         this IEnumerable<(double longitude, double latitude, float? e)> lineString) | 
|   | 312 | 68 |  |     { | 
|   | 312 | 69 |  |         var distance = 0.0; | 
|   |  | 70 |  |  | 
|   | 312 | 71 |  |         using var shapeEnumerator = lineString.GetEnumerator(); | 
|   | 312 | 72 |  |         shapeEnumerator.MoveNext(); | 
|   | 312 | 73 |  |         var previous = shapeEnumerator.Current; | 
|   |  | 74 |  |  | 
|   | 714 | 75 |  |         while (shapeEnumerator.MoveNext()) | 
|   | 402 | 76 |  |         { | 
|   | 402 | 77 |  |             var current = shapeEnumerator.Current; | 
|   | 402 | 78 |  |             distance += previous.DistanceEstimateInMeter(current); | 
|   | 402 | 79 |  |             previous = current; | 
|   | 402 | 80 |  |         } | 
|   |  | 81 |  |  | 
|   | 312 | 82 |  |         return distance; | 
|   | 312 | 83 |  |     } | 
|   |  | 84 |  |  | 
|   |  | 85 |  |     /// <summary> | 
|   |  | 86 |  |     /// Returns a coordinate offset with a given distance. | 
|   |  | 87 |  |     /// </summary> | 
|   |  | 88 |  |     /// <param name="coordinate">The coordinate.</param> | 
|   |  | 89 |  |     /// <param name="meter">The distance.</param> | 
|   |  | 90 |  |     /// <returns>An offset coordinate.</returns> | 
|   |  | 91 |  |     public static (double longitude, double latitude, float? e) OffsetWithDistanceX( | 
|   |  | 92 |  |         this (double longitude, double latitude, float? e) coordinate, double meter) | 
|   | 46 | 93 |  |     { | 
|   |  | 94 |  |         const double offset = 0.001; | 
|   | 46 | 95 |  |         var offsetLon = (coordinate.longitude + offset, coordinate.latitude).AddElevation(coordinate.e); | 
|   | 46 | 96 |  |         var lonDistance = offsetLon.DistanceEstimateInMeter(coordinate); | 
|   |  | 97 |  |  | 
|   | 46 | 98 |  |         return (coordinate.longitude + (meter / lonDistance * offset), | 
|   | 46 | 99 |  |             coordinate.latitude).AddElevation(coordinate.e); | 
|   | 46 | 100 |  |     } | 
|   |  | 101 |  |  | 
|   |  | 102 |  |     /// <summary> | 
|   |  | 103 |  |     /// Returns a coordinate offset with a given distance. | 
|   |  | 104 |  |     /// </summary> | 
|   |  | 105 |  |     /// <param name="coordinate">The coordinate.</param> | 
|   |  | 106 |  |     /// <param name="meter">The distance.</param> | 
|   |  | 107 |  |     /// <returns>An offset coordinate.</returns> | 
|   |  | 108 |  |     public static (double longitude, double latitude, float? e) OffsetWithDistanceY( | 
|   |  | 109 |  |         this (double longitude, double latitude, float? e) coordinate, | 
|   |  | 110 |  |         double meter) | 
|   | 40 | 111 |  |     { | 
|   |  | 112 |  |         const double offset = 0.001; | 
|   | 40 | 113 |  |         var offsetLat = (coordinate.longitude, coordinate.latitude + offset).AddElevation(coordinate.e); | 
|   | 40 | 114 |  |         var latDistance = offsetLat.DistanceEstimateInMeter(coordinate); | 
|   |  | 115 |  |  | 
|   | 40 | 116 |  |         return (coordinate.longitude, | 
|   | 40 | 117 |  |             coordinate.latitude + (meter / latDistance * offset)).AddElevation(coordinate.e); | 
|   | 40 | 118 |  |     } | 
|   |  | 119 |  |  | 
|   |  | 120 |  |     /// <summary> | 
|   |  | 121 |  |     /// Calculates an offset position along the line segment. | 
|   |  | 122 |  |     /// </summary> | 
|   |  | 123 |  |     /// <param name="line">The line segment.</param> | 
|   |  | 124 |  |     /// <param name="position">The position in meters relative to the start point.</param> | 
|   |  | 125 |  |     /// <returns>The offset coordinate.</returns> | 
|   |  | 126 |  |     public static (double longitude, double latitude, float? e) PositionAlongLineInMeters( | 
|   |  | 127 |  |         this IEnumerable<(double longitude, double latitude, float? e)> line, double position) | 
|   | 0 | 128 |  |     { | 
|   |  | 129 |  |         // ReSharper disable once PossibleMultipleEnumeration | 
|   | 0 | 130 |  |         var length = line.DistanceEstimateInMeter(); | 
|   |  | 131 |  |  | 
|   |  | 132 |  |         // ReSharper disable once PossibleMultipleEnumeration | 
|   | 0 | 133 |  |         return line.PositionAlongLineInMeters(length, position); | 
|   | 0 | 134 |  |     } | 
|   |  | 135 |  |  | 
|   |  | 136 |  |     /// <summary> | 
|   |  | 137 |  |     /// Calculates an offset position along the line segment. | 
|   |  | 138 |  |     /// </summary> | 
|   |  | 139 |  |     /// <param name="line">The line segment.</param> | 
|   |  | 140 |  |     /// <param name="offset">The offset [0,1].</param> | 
|   |  | 141 |  |     /// <returns>The offset coordinate.</returns> | 
|   |  | 142 |  |     public static (double longitude, double latitude, float? e) PositionAlongLine( | 
|   |  | 143 |  |         this IEnumerable<(double longitude, double latitude, float? e)> line, double offset) | 
|   | 0 | 144 |  |     { | 
|   |  | 145 |  |         // ReSharper disable once PossibleMultipleEnumeration | 
|   | 0 | 146 |  |         var length = line.DistanceEstimateInMeter(); | 
|   | 0 | 147 |  |         var targetLength = length * (offset / (double)ushort.MaxValue); | 
|   |  | 148 |  |  | 
|   |  | 149 |  |         // ReSharper disable once PossibleMultipleEnumeration | 
|   | 0 | 150 |  |         return line.PositionAlongLineInMeters(length, targetLength); | 
|   | 0 | 151 |  |     } | 
|   |  | 152 |  |  | 
|   |  | 153 |  |     private static (double longitude, double latitude, float? e) PositionAlongLineInMeters( | 
|   |  | 154 |  |         this IEnumerable<(double longitude, double latitude, float? e)> line, double length, double targetLength) | 
|   | 0 | 155 |  |     { | 
|   | 0 | 156 |  |         var currentLength = 0.0; | 
|   |  | 157 |  |  | 
|   |  | 158 |  |         // ReSharper disable once PossibleMultipleEnumeration | 
|   | 0 | 159 |  |         using var enumerator = line.GetEnumerator(); | 
|   | 0 | 160 |  |         if (!enumerator.MoveNext()) throw new Exception("Line doesn't have 2 locations"); | 
|   | 0 | 161 |  |         var previous = enumerator.Current; | 
|   | 0 | 162 |  |         while (enumerator.MoveNext()) | 
|   | 0 | 163 |  |         { | 
|   | 0 | 164 |  |             var current = enumerator.Current; | 
|   | 0 | 165 |  |             var segmentLength = current.DistanceEstimateInMeter(previous); | 
|   |  | 166 |  |  | 
|   |  | 167 |  |             // check if the target is in this segment or not. | 
|   | 0 | 168 |  |             if (segmentLength + currentLength < targetLength) | 
|   | 0 | 169 |  |             { | 
|   | 0 | 170 |  |                 currentLength += segmentLength; | 
|   | 0 | 171 |  |                 previous = current; | 
|   | 0 | 172 |  |                 continue; | 
|   |  | 173 |  |             } | 
|   |  | 174 |  |  | 
|   | 0 | 175 |  |             var segmentOffsetLength = segmentLength + currentLength - targetLength; | 
|   | 0 | 176 |  |             var segmentOffset = 1 - (segmentOffsetLength / segmentLength); | 
|   |  | 177 |  |  | 
|   | 0 | 178 |  |             float? e = null; | 
|   | 0 | 179 |  |             if (previous.e.HasValue && | 
|   | 0 | 180 |  |                 current.e.HasValue) | 
|   | 0 | 181 |  |             { | 
|   | 0 | 182 |  |                 e = (float)(previous.e.Value + (segmentOffset * (current.e.Value - previous.e.Value))); | 
|   | 0 | 183 |  |             } | 
|   |  | 184 |  |  | 
|   | 0 | 185 |  |             return (previous.longitude + (segmentOffset * (current.longitude - previous.longitude)), | 
|   | 0 | 186 |  |                 previous.latitude + (segmentOffset * (current.latitude - previous.latitude)), e); | 
|   |  | 187 |  |         } | 
|   |  | 188 |  |  | 
|   | 0 | 189 |  |         return previous; | 
|   | 0 | 190 |  |     } | 
|   |  | 191 |  |  | 
|   |  | 192 |  |     /// <summary> | 
|   |  | 193 |  |     /// Calculates an offset position along the line segment. | 
|   |  | 194 |  |     /// </summary> | 
|   |  | 195 |  |     /// <param name="line">The line segment.</param> | 
|   |  | 196 |  |     /// <param name="offset">The offset [0,1].</param> | 
|   |  | 197 |  |     /// <returns>The offset coordinate.</returns> | 
|   |  | 198 |  |     public static (double longitude, double latitude, float? e) PositionAlongLine( | 
|   |  | 199 |  |         this ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 200 |  |             (double longitude, double latitude, float? e) coordinate2) line, double offset) | 
|   | 0 | 201 |  |     { | 
|   | 0 | 202 |  |         var coordinate1 = line.coordinate1; | 
|   | 0 | 203 |  |         var coordinate2 = line.coordinate2; | 
|   |  | 204 |  |  | 
|   | 0 | 205 |  |         var latitude = coordinate1.latitude + ((coordinate2.latitude - coordinate1.latitude) * offset); | 
|   | 0 | 206 |  |         var longitude = coordinate1.longitude + ((coordinate2.longitude - coordinate1.longitude) * offset); | 
|   | 0 | 207 |  |         float? e = null; | 
|   | 0 | 208 |  |         if (coordinate1.e.HasValue && | 
|   | 0 | 209 |  |             coordinate2.e.HasValue) | 
|   | 0 | 210 |  |         { | 
|   | 0 | 211 |  |             e = (float)(coordinate1.e.Value - ((coordinate2.e.Value - coordinate1.e.Value) * offset)); | 
|   | 0 | 212 |  |         } | 
|   |  | 213 |  |  | 
|   | 0 | 214 |  |         return (longitude, latitude).AddElevation(e); | 
|   | 0 | 215 |  |     } | 
|   |  | 216 |  |  | 
|   |  | 217 |  |     private const double E = 0.0000000001; | 
|   |  | 218 |  |  | 
|   |  | 219 |  |     /// <summary> | 
|   |  | 220 |  |     /// Projects for coordinate on this line. | 
|   |  | 221 |  |     /// </summary> | 
|   |  | 222 |  |     /// <param name="line">The line.</param> | 
|   |  | 223 |  |     /// <param name="coordinate">The coordinate.</param> | 
|   |  | 224 |  |     /// <returns>The project coordinate.</returns> | 
|   |  | 225 |  |     public static (double longitude, double latitude, float? e)? ProjectOn( | 
|   |  | 226 |  |         this ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 227 |  |             (double longitude, double latitude, float? e) coordinate2) line, | 
|   |  | 228 |  |         (double longitude, double latitude, float? e) coordinate) | 
|   | 38 | 229 |  |     { | 
|   | 38 | 230 |  |         var coordinate1 = line.coordinate1; | 
|   | 38 | 231 |  |         var coordinate2 = line.coordinate2; | 
|   |  | 232 |  |  | 
|   |  | 233 |  |         // TODO: do we need to calculate the expensive length in meter, this can be done more easily. | 
|   | 38 | 234 |  |         var lengthInMeters = line.coordinate1.DistanceEstimateInMeter(line.coordinate2); | 
|   | 38 | 235 |  |         if (lengthInMeters < E) | 
|   | 0 | 236 |  |         { | 
|   | 0 | 237 |  |             return null; | 
|   |  | 238 |  |         } | 
|   |  | 239 |  |  | 
|   |  | 240 |  |         // get direction vector. | 
|   | 38 | 241 |  |         var diffLat = coordinate2.latitude - coordinate1.latitude; | 
|   | 38 | 242 |  |         var diffLon = coordinate2.longitude - coordinate1.longitude; | 
|   |  | 243 |  |  | 
|   |  | 244 |  |         // increase this line in length if needed. | 
|   | 38 | 245 |  |         var longerLine = line; | 
|   | 38 | 246 |  |         if (lengthInMeters < 50) | 
|   | 16 | 247 |  |         { | 
|   | 16 | 248 |  |             longerLine = (coordinate1, (diffLon + coordinate.longitude, diffLat + coordinate.latitude, null)); | 
|   | 16 | 249 |  |         } | 
|   |  | 250 |  |  | 
|   |  | 251 |  |         // rotate 90°, offset y with x, and x with y. | 
|   | 38 | 252 |  |         var xLength = longerLine.coordinate1.DistanceEstimateInMeter((longerLine.coordinate2.longitude, | 
|   | 38 | 253 |  |             longerLine.coordinate1.latitude, null)); | 
|   | 38 | 254 |  |         if (longerLine.coordinate1.longitude > longerLine.coordinate2.longitude) | 
|   | 10 | 255 |  |         { | 
|   | 10 | 256 |  |             xLength = -xLength; | 
|   | 10 | 257 |  |         } | 
|   |  | 258 |  |  | 
|   | 38 | 259 |  |         var yLength = longerLine.coordinate1.DistanceEstimateInMeter((longerLine.coordinate1.longitude, | 
|   | 38 | 260 |  |             longerLine.coordinate2.latitude, null)); | 
|   | 38 | 261 |  |         if (longerLine.coordinate1.latitude > longerLine.coordinate2.latitude) | 
|   | 18 | 262 |  |         { | 
|   | 18 | 263 |  |             yLength = -yLength; | 
|   | 18 | 264 |  |         } | 
|   |  | 265 |  |  | 
|   | 38 | 266 |  |         var second = coordinate.OffsetWithDistanceY(xLength) | 
|   | 38 | 267 |  |             .OffsetWithDistanceX(-yLength); | 
|   |  | 268 |  |  | 
|   |  | 269 |  |         // create a second line. | 
|   | 38 | 270 |  |         var other = (coordinate, second); | 
|   |  | 271 |  |  | 
|   |  | 272 |  |         // calculate intersection. | 
|   | 38 | 273 |  |         var projected = longerLine.Intersect(other, false); | 
|   |  | 274 |  |  | 
|   |  | 275 |  |         // check if coordinate is on this line. | 
|   | 38 | 276 |  |         if (!projected.HasValue) | 
|   | 0 | 277 |  |         { | 
|   | 0 | 278 |  |             return null; | 
|   |  | 279 |  |         } | 
|   |  | 280 |  |  | 
|   |  | 281 |  |         // check if the coordinate is on this line. | 
|   | 38 | 282 |  |         var dist = (line.A() * line.A()) + (line.B() * line.B()); | 
|   | 38 | 283 |  |         var line1 = (projected.Value, coordinate1); | 
|   | 38 | 284 |  |         var distTo1 = (line1.A() * line1.A()) + (line1.B() * line1.B()); | 
|   | 38 | 285 |  |         if (distTo1 > dist) | 
|   | 18 | 286 |  |         { | 
|   | 18 | 287 |  |             return null; | 
|   |  | 288 |  |         } | 
|   |  | 289 |  |  | 
|   | 20 | 290 |  |         var line2 = (projected.Value, coordinate2); | 
|   | 20 | 291 |  |         var distTo2 = (line2.A() * line2.A()) + (line2.B() * line2.B()); | 
|   | 20 | 292 |  |         if (distTo2 > dist) | 
|   | 2 | 293 |  |         { | 
|   | 2 | 294 |  |             return null; | 
|   |  | 295 |  |         } | 
|   |  | 296 |  |  | 
|   | 18 | 297 |  |         return projected; | 
|   | 38 | 298 |  |     } | 
|   |  | 299 |  |  | 
|   |  | 300 |  |     /// <summary> | 
|   |  | 301 |  |     /// Returns the center of the box. | 
|   |  | 302 |  |     /// </summary> | 
|   |  | 303 |  |     /// <param name="box">The box.</param> | 
|   |  | 304 |  |     /// <returns>The center.</returns> | 
|   |  | 305 |  |     public static (double longitude, double latitude, float? e) Center( | 
|   |  | 306 |  |         this ((double longitude, double latitude, float? e) topLeft, (double longitude, double latitude, float? e) | 
|   |  | 307 |  |             bottomRight) box) | 
|   | 30 | 308 |  |     { | 
|   | 30 | 309 |  |         float? e = null; | 
|   | 30 | 310 |  |         if (box.topLeft.e.HasValue && | 
|   | 30 | 311 |  |             box.bottomRight.e.HasValue) | 
|   | 0 | 312 |  |         { | 
|   | 0 | 313 |  |             e = box.topLeft.e.Value + box.bottomRight.e.Value; | 
|   | 0 | 314 |  |         } | 
|   |  | 315 |  |  | 
|   | 30 | 316 |  |         return ((box.topLeft.longitude + box.bottomRight.longitude) / 2, | 
|   | 30 | 317 |  |             (box.topLeft.latitude + box.bottomRight.latitude) / 2).AddElevation(e); | 
|   | 30 | 318 |  |     } | 
|   |  | 319 |  |  | 
|   |  | 320 |  |     /// <summary> | 
|   |  | 321 |  |     /// Expands the given box with the other box to encompass both. | 
|   |  | 322 |  |     /// </summary> | 
|   |  | 323 |  |     /// <param name="box">The original box.</param> | 
|   |  | 324 |  |     /// <param name="other">The other box.</param> | 
|   |  | 325 |  |     /// <returns>The expand box or the original box if the other was already contained.</returns> | 
|   |  | 326 |  |     public static ((double longitude, double latitude, float? e) topLeft, (double longitude, double latitude, float? | 
|   |  | 327 |  |         e) bottomRight) | 
|   |  | 328 |  |         Expand( | 
|   |  | 329 |  |             this ((double longitude, double latitude, float? e) topLeft, (double longitude, double latitude, float? | 
|   |  | 330 |  |                 e) bottomRight) box, | 
|   |  | 331 |  |             ((double longitude, double latitude, float? e) topLeft, (double longitude, double latitude, float? e) | 
|   |  | 332 |  |                 bottomRight) other) | 
|   | 0 | 333 |  |     { | 
|   | 0 | 334 |  |         if (!box.Overlaps(other.topLeft)) | 
|   | 0 | 335 |  |         { | 
|   | 0 | 336 |  |             var center = box.Center(); | 
|   |  | 337 |  |  | 
|   |  | 338 |  |             // handle left. | 
|   | 0 | 339 |  |             var left = box.topLeft.longitude; | 
|   | 0 | 340 |  |             if (!box.Overlaps((other.topLeft.longitude, center.latitude, null))) | 
|   | 0 | 341 |  |             { | 
|   | 0 | 342 |  |                 left = other.topLeft.longitude; | 
|   | 0 | 343 |  |             } | 
|   |  | 344 |  |  | 
|   |  | 345 |  |             // handle top. | 
|   | 0 | 346 |  |             var top = box.topLeft.latitude; | 
|   | 0 | 347 |  |             if (!box.Overlaps((center.longitude, other.topLeft.latitude, null))) | 
|   | 0 | 348 |  |             { | 
|   | 0 | 349 |  |                 top = other.topLeft.latitude; | 
|   | 0 | 350 |  |             } | 
|   |  | 351 |  |  | 
|   | 0 | 352 |  |             box = ((left, top, null), box.bottomRight); | 
|   | 0 | 353 |  |         } | 
|   |  | 354 |  |  | 
|   | 0 | 355 |  |         if (!box.Overlaps(other.bottomRight)) | 
|   | 0 | 356 |  |         { | 
|   | 0 | 357 |  |             var center = box.Center(); | 
|   |  | 358 |  |  | 
|   |  | 359 |  |             // handle right. | 
|   | 0 | 360 |  |             var right = box.bottomRight.longitude; | 
|   | 0 | 361 |  |             if (!box.Overlaps((other.bottomRight.longitude, center.latitude, null))) | 
|   | 0 | 362 |  |             { | 
|   | 0 | 363 |  |                 right = other.bottomRight.longitude; | 
|   | 0 | 364 |  |             } | 
|   |  | 365 |  |  | 
|   |  | 366 |  |             // handle bottom. | 
|   | 0 | 367 |  |             var bottom = box.bottomRight.latitude; | 
|   | 0 | 368 |  |             if (!box.Overlaps((center.longitude, other.bottomRight.latitude, null))) | 
|   | 0 | 369 |  |             { | 
|   | 0 | 370 |  |                 bottom = other.bottomRight.latitude; | 
|   | 0 | 371 |  |             } | 
|   |  | 372 |  |  | 
|   | 0 | 373 |  |             box = (box.topLeft, (right, bottom, null)); | 
|   | 0 | 374 |  |         } | 
|   |  | 375 |  |  | 
|   | 0 | 376 |  |         return box; | 
|   | 0 | 377 |  |     } | 
|   |  | 378 |  |  | 
|   |  | 379 |  |     /// <summary> | 
|   |  | 380 |  |     /// Calculates the intersection point of the given line with this line. | 
|   |  | 381 |  |     /// | 
|   |  | 382 |  |     /// Returns null if the lines have the same direction or don't intersect. | 
|   |  | 383 |  |     /// | 
|   |  | 384 |  |     /// Assumes the given line is not a segment and this line is a segment. | 
|   |  | 385 |  |     /// </summary> | 
|   |  | 386 |  |     public static (double longitude, double latitude, float? e)? Intersect( | 
|   |  | 387 |  |         this ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 388 |  |             (double longitude, double latitude, float? e) coordinate2) thisLine, | 
|   |  | 389 |  |         ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 390 |  |             (double longitude, double latitude, float? e) coordinate2) line, bool checkSegment = true) | 
|   | 42 | 391 |  |     { | 
|   | 42 | 392 |  |         var det = (double)((line.A() * thisLine.B()) - (thisLine.A() * line.B())); | 
|   | 42 | 393 |  |         if (Math.Abs(det) <= E) | 
|   | 1 | 394 |  |         { | 
|   |  | 395 |  |             // lines are parallel; no intersections. | 
|   | 1 | 396 |  |             return null; | 
|   |  | 397 |  |         } | 
|   |  | 398 |  |         else | 
|   | 41 | 399 |  |         { | 
|   |  | 400 |  |             // lines are not the same and not parallel so they will intersect. | 
|   | 41 | 401 |  |             var x = ((thisLine.B() * line.C()) - (line.B() * thisLine.C())) / det; | 
|   | 41 | 402 |  |             var y = ((line.A() * thisLine.C()) - (thisLine.A() * line.C())) / det; | 
|   |  | 403 |  |  | 
|   | 41 | 404 |  |             (double latitude, double longitude, float? e) coordinate = (x, y, (float?)null); | 
|   |  | 405 |  |  | 
|   |  | 406 |  |             // check if the coordinate is on this line. | 
|   | 41 | 407 |  |             if (checkSegment) | 
|   | 3 | 408 |  |             { | 
|   | 3 | 409 |  |                 var dist = (thisLine.A() * thisLine.A()) + (thisLine.B() * thisLine.B()); | 
|   | 3 | 410 |  |                 var line1 = (coordinate, thisLine.coordinate1); | 
|   | 3 | 411 |  |                 var distTo1 = (line1.A() * line1.A()) + (line1.B() * line1.B()); | 
|   | 3 | 412 |  |                 if (distTo1 > dist) | 
|   | 1 | 413 |  |                 { | 
|   | 1 | 414 |  |                     return null; | 
|   |  | 415 |  |                 } | 
|   |  | 416 |  |  | 
|   | 2 | 417 |  |                 var line2 = (coordinate, thisLine.coordinate2); | 
|   | 2 | 418 |  |                 var distTo2 = (line2.A() * line2.A()) + (line2.B() * line2.B()); | 
|   | 2 | 419 |  |                 if (distTo2 > dist) | 
|   | 1 | 420 |  |                 { | 
|   | 1 | 421 |  |                     return null; | 
|   |  | 422 |  |                 } | 
|   | 1 | 423 |  |             } | 
|   |  | 424 |  |  | 
|   | 39 | 425 |  |             if (thisLine.coordinate1.e == null || thisLine.coordinate2.e == null) | 
|   | 39 | 426 |  |             { | 
|   | 39 | 427 |  |                 return coordinate; | 
|   |  | 428 |  |             } | 
|   |  | 429 |  |  | 
|   | 0 | 430 |  |             float? e = null; | 
|   | 0 | 431 |  |             if (Math.Abs(thisLine.coordinate1.e.Value - thisLine.coordinate2.e.Value) < E) | 
|   | 0 | 432 |  |             { | 
|   |  | 433 |  |                 // don't calculate anything, elevation is identical. | 
|   | 0 | 434 |  |                 e = thisLine.coordinate1.e; | 
|   | 0 | 435 |  |             } | 
|   | 0 | 436 |  |             else if (Math.Abs(thisLine.A()) < E && Math.Abs(thisLine.B()) < E) | 
|   | 0 | 437 |  |             { | 
|   |  | 438 |  |                 // tiny segment, not stable to calculate offset | 
|   | 0 | 439 |  |                 e = thisLine.coordinate1.e; | 
|   | 0 | 440 |  |             } | 
|   |  | 441 |  |             else | 
|   | 0 | 442 |  |             { | 
|   |  | 443 |  |                 // calculate offset and calculate an estimate of the elevation. | 
|   | 0 | 444 |  |                 if (Math.Abs(thisLine.A()) > Math.Abs(thisLine.B())) | 
|   | 0 | 445 |  |                 { | 
|   | 0 | 446 |  |                     var diffLat = Math.Abs(thisLine.A()); | 
|   | 0 | 447 |  |                     var diffLatIntersection = Math.Abs(coordinate.latitude - thisLine.coordinate1.latitude); | 
|   |  | 448 |  |  | 
|   | 0 | 449 |  |                     e = (float)(((thisLine.coordinate2.e - thisLine.coordinate1.e) * | 
|   | 0 | 450 |  |                                  (diffLatIntersection / diffLat)) + | 
|   | 0 | 451 |  |                                 thisLine.coordinate1.e); | 
|   | 0 | 452 |  |                 } | 
|   |  | 453 |  |                 else | 
|   | 0 | 454 |  |                 { | 
|   | 0 | 455 |  |                     var diffLon = Math.Abs(thisLine.B()); | 
|   | 0 | 456 |  |                     var diffLonIntersection = Math.Abs(coordinate.longitude - thisLine.coordinate1.longitude); | 
|   |  | 457 |  |  | 
|   | 0 | 458 |  |                     e = (float)(((thisLine.coordinate2.e - thisLine.coordinate1.e) * | 
|   | 0 | 459 |  |                                  (diffLonIntersection / diffLon)) + | 
|   | 0 | 460 |  |                                 thisLine.coordinate1.e); | 
|   | 0 | 461 |  |                 } | 
|   | 0 | 462 |  |             } | 
|   |  | 463 |  |  | 
|   | 0 | 464 |  |             return coordinate.AddElevation(e); | 
|   |  | 465 |  |         } | 
|   | 42 | 466 |  |     } | 
|   |  | 467 |  |  | 
|   |  | 468 |  |     private static double A(this ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 469 |  |         (double longitude, double latitude, float? e) coordinate2) line) | 
|   | 538 | 470 |  |     { | 
|   | 538 | 471 |  |         return line.coordinate2.latitude - line.coordinate1.latitude; | 
|   | 538 | 472 |  |     } | 
|   |  | 473 |  |  | 
|   |  | 474 |  |     private static double B(this ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 475 |  |         (double longitude, double latitude, float? e) coordinate2) line) | 
|   | 538 | 476 |  |     { | 
|   | 538 | 477 |  |         return line.coordinate1.longitude - line.coordinate2.longitude; | 
|   | 538 | 478 |  |     } | 
|   |  | 479 |  |  | 
|   |  | 480 |  |     private static double C(this ((double longitude, double latitude, float? e) coordinate1, | 
|   |  | 481 |  |         (double longitude, double latitude, float? e) coordinate2) line) | 
|   | 164 | 482 |  |     { | 
|   | 164 | 483 |  |         return (line.A() * line.coordinate1.longitude) + | 
|   | 164 | 484 |  |                (line.B() * line.coordinate1.latitude); | 
|   | 164 | 485 |  |     } | 
|   |  | 486 |  |  | 
|   |  | 487 |  |     /// <summary> | 
|   |  | 488 |  |     /// Creates a box around this coordinate with width/height approximately the given size in meter. | 
|   |  | 489 |  |     /// </summary> | 
|   |  | 490 |  |     /// <param name="coordinate">The coordinate.</param> | 
|   |  | 491 |  |     /// <param name="sizeInMeters">The size in meter.</param> | 
|   |  | 492 |  |     /// <returns>The size in meter.</returns> | 
|   |  | 493 |  |     public static ((double longitude, double latitude, float? e) topLeft, (double longitude, double latitude, float? | 
|   |  | 494 |  |         e) bottomRight) | 
|   |  | 495 |  |         BoxAround(this (double longitude, double latitude, float? e) coordinate, double sizeInMeters) | 
|   | 21 | 496 |  |     { | 
|   | 21 | 497 |  |         var offsetLat = (coordinate.longitude, coordinate.latitude + 0.1, (float?)null); | 
|   | 21 | 498 |  |         var offsetLon = (coordinate.longitude + 0.1, coordinate.latitude, (float?)null); | 
|   | 21 | 499 |  |         var latDistance = offsetLat.DistanceEstimateInMeter(coordinate); | 
|   | 21 | 500 |  |         var lonDistance = offsetLon.DistanceEstimateInMeter(coordinate); | 
|   |  | 501 |  |  | 
|   | 21 | 502 |  |         return ((coordinate.longitude - (sizeInMeters / lonDistance * 0.1), | 
|   | 21 | 503 |  |                 coordinate.latitude + (sizeInMeters / latDistance * 0.1), null), | 
|   | 21 | 504 |  |             (coordinate.longitude + (sizeInMeters / lonDistance * 0.1), | 
|   | 21 | 505 |  |                 coordinate.latitude - (sizeInMeters / latDistance * 0.1), null)); | 
|   | 21 | 506 |  |     } | 
|   |  | 507 |  |  | 
|   |  | 508 |  |     /// <summary> | 
|   |  | 509 |  |     /// Returns true if the box overlaps the given coordinate. | 
|   |  | 510 |  |     /// </summary> | 
|   |  | 511 |  |     /// <param name="box">The box.</param> | 
|   |  | 512 |  |     /// <param name="coordinate">The coordinate.</param> | 
|   |  | 513 |  |     /// <returns>True of the coordinate is inside the bounding box.</returns> | 
|   |  | 514 |  |     public static bool Overlaps( | 
|   |  | 515 |  |         this ((double longitude, double latitude, float? e) topLeft, (double longitude, double latitude, float? e) | 
|   |  | 516 |  |             bottomRight) box, | 
|   |  | 517 |  |         (double longitude, double latitude, float? e) coordinate) | 
|   | 70 | 518 |  |     { | 
|   | 70 | 519 |  |         return box.bottomRight.latitude < coordinate.latitude && coordinate.latitude <= box.topLeft.latitude && | 
|   | 70 | 520 |  |                box.topLeft.longitude < coordinate.longitude && coordinate.longitude <= box.bottomRight.longitude; | 
|   | 70 | 521 |  |     } | 
|   |  | 522 |  |  | 
|   |  | 523 |  |     /// <summary> | 
|   |  | 524 |  |     /// Given two WGS84 coordinates, if walking from c1 to c2, it gives the angle that one would be following. | 
|   |  | 525 |  |     /// | 
|   |  | 526 |  |     /// 0° is north, 90° is east, -90° is west, both 180 and -180 are south | 
|   |  | 527 |  |     /// </summary> | 
|   |  | 528 |  |     /// <param name="c1">The first coordinate.</param> | 
|   |  | 529 |  |     /// <param name="c2">The second coordinate.</param> | 
|   |  | 530 |  |     /// <returns>The angle with the meridian in Northern direction.</returns> | 
|   |  | 531 |  |     public static double AngleWithMeridian(this (double longitude, double latitude, float? e) c1, | 
|   |  | 532 |  |         (double longitude, double latitude, float? e) c2) | 
|   | 212 | 533 |  |     { | 
|   | 212 | 534 |  |         var dy = c2.latitude - c1.latitude; | 
|   | 212 | 535 |  |         var dx = Math.Cos(Math.PI / 180 * c1.latitude) * (c2.longitude - c1.longitude); | 
|   |  | 536 |  |         // phi is the angle we search, but with 0 pointing eastwards and in radians | 
|   | 212 | 537 |  |         var phi = Math.Atan2(dy, dx); | 
|   | 212 | 538 |  |         var angle = | 
|   | 212 | 539 |  |             (phi - (Math.PI / 2)) // Rotate 90° to have the north up | 
|   | 212 | 540 |  |             * 180 / Math.PI; // Convert to degrees | 
|   | 212 | 541 |  |         angle = -angle; | 
|   |  | 542 |  |         // A bit of normalization below: | 
|   | 212 | 543 |  |         if (angle < -180) | 
|   | 0 | 544 |  |         { | 
|   | 0 | 545 |  |             angle += 360; | 
|   | 0 | 546 |  |         } | 
|   |  | 547 |  |  | 
|   | 212 | 548 |  |         if (angle > 180) | 
|   | 92 | 549 |  |         { | 
|   | 92 | 550 |  |             angle -= 360; | 
|   | 92 | 551 |  |         } | 
|   |  | 552 |  |  | 
|   | 212 | 553 |  |         return angle; | 
|   | 212 | 554 |  |     } | 
|   |  | 555 |  | } |