forked from mattjohnsonpint/GeoTimeZone
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
42adc24
commit d87fa78
Showing
21 changed files
with
841 additions
and
858 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs | ||
############################### | ||
# Core EditorConfig Options # | ||
############################### | ||
|
||
# All files | ||
[*] | ||
indent_style = space | ||
insert_final_newline = true | ||
charset = utf-8 | ||
|
||
# Project files | ||
[*.{csproj,props,targets}] | ||
indent_size = 2 | ||
|
||
# Code files | ||
[*.{cs}] | ||
indent_size = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,36 @@ | ||
using System; | ||
using System.Diagnostics; | ||
|
||
namespace GeoTimeZone.DataBuilder | ||
namespace GeoTimeZone.DataBuilder; | ||
|
||
public static class ConsoleOutput | ||
{ | ||
public class ConsoleOutput | ||
{ | ||
private readonly Stopwatch _sw = new Stopwatch(); | ||
private static readonly Stopwatch Stopwatch = new(); | ||
|
||
public void Start() | ||
{ | ||
_sw.Start(); | ||
Console.WriteLine("Started at: {0}", GetLocalDisplayTime()); | ||
Console.WriteLine(); | ||
} | ||
public static void Start() | ||
{ | ||
Stopwatch.Start(); | ||
Console.WriteLine("Started at: {0}", GetLocalDisplayTime()); | ||
Console.WriteLine(); | ||
} | ||
|
||
public void Stop() | ||
{ | ||
_sw.Stop(); | ||
Console.WriteLine(); | ||
Console.WriteLine("Finished at: {0}", GetLocalDisplayTime()); | ||
Console.WriteLine("Total elapsed time: {0:hh\\:mm\\:ss\\.fff}", _sw.Elapsed); | ||
Console.WriteLine(); | ||
Console.WriteLine("Hit any key to exit."); | ||
Console.ReadKey(); | ||
} | ||
public static void Stop() | ||
{ | ||
Stopwatch.Stop(); | ||
Console.WriteLine(); | ||
Console.WriteLine("Finished at: {0}", GetLocalDisplayTime()); | ||
Console.WriteLine("Total elapsed time: {0:hh\\:mm\\:ss\\.fff}", Stopwatch.Elapsed); | ||
Console.WriteLine(); | ||
Console.WriteLine("Hit any key to exit."); | ||
Console.ReadKey(); | ||
} | ||
|
||
public void WriteMessage(string message) | ||
{ | ||
Console.WriteLine("{0} - {1}", GetLocalDisplayTime(), message); | ||
} | ||
public static void WriteMessage(string message) | ||
{ | ||
Console.WriteLine("{0} - {1}", GetLocalDisplayTime(), message); | ||
} | ||
|
||
private static string GetLocalDisplayTime() | ||
{ | ||
return DateTime.Now.ToString("HH:mm:ss.fff"); | ||
} | ||
private static string GetLocalDisplayTime() | ||
{ | ||
return DateTime.Now.ToString("HH:mm:ss.fff"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,140 +1,131 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using NetTopologySuite.Geometries; | ||
using NetTopologySuite.Geometries.Prepared; | ||
|
||
namespace GeoTimeZone.DataBuilder | ||
namespace GeoTimeZone.DataBuilder; | ||
|
||
public class GeohashTree : List<GeohashTreeNode> | ||
{ | ||
public class GeohashTree : List<GeohashTreeNode> | ||
internal const int Precision = 5; | ||
|
||
internal const string Base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; | ||
|
||
public GeohashTree() | ||
{ | ||
public const string Base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; | ||
AddRange(GetNextLevel()); | ||
} | ||
|
||
public GeohashTree() | ||
{ | ||
AddRange(GetNextLevel()); | ||
} | ||
public List<string> GetGeohashes(IPreparedGeometry geometry) => this.SelectMany(level => GetGeohashes(geometry, level)).ToList(); | ||
|
||
public string[] GetGeohashes(Geometry geometry) | ||
{ | ||
return this.SelectMany(level => GetGeohashes(geometry, level)).ToArray(); | ||
} | ||
|
||
private static IEnumerable<string> GetGeohashes(Geometry geometry, GeohashTreeNode level) | ||
private static IEnumerable<string> GetGeohashes(IPreparedGeometry geometry, GeohashTreeNode level) | ||
{ | ||
try | ||
{ | ||
try | ||
{ | ||
Geometry env = level.Geometry; | ||
|
||
if (geometry.Contains(env)) | ||
{ | ||
return new[] {level.Geohash}; | ||
} | ||
|
||
if (!geometry.Intersects(env)) | ||
{ | ||
return new string[0]; | ||
} | ||
|
||
if (level.Geohash.Length == 5) | ||
{ | ||
return new[] {level.Geohash}; | ||
} | ||
|
||
return level.GetChildren().SelectMany(child => GetGeohashes(geometry, child)); | ||
} | ||
catch | ||
var envelope = level.GetGeometry(); | ||
if (geometry.Contains(envelope)) | ||
{ | ||
// Ignore errors caused by invalid geometry | ||
return new string[0]; | ||
return new[] {level.Geohash}; | ||
} | ||
} | ||
|
||
public GeohashTreeNode GetTreeNode(string geohash) | ||
{ | ||
if (string.IsNullOrWhiteSpace(geohash)) | ||
if (!geometry.Intersects(envelope)) | ||
{ | ||
return null; | ||
return Array.Empty<string>(); | ||
} | ||
|
||
GeohashTreeNode result = null; | ||
foreach (char c in geohash) | ||
if (level.Geohash.Length == Precision) | ||
{ | ||
if (c == '-') | ||
{ | ||
return result; | ||
} | ||
|
||
int index = Base32.IndexOf(c); | ||
result = result == null ? this[index] : result.GetChildren()[index]; | ||
return new[] {level.Geohash}; | ||
} | ||
|
||
return result; | ||
return level.GetChildren().SelectMany(child => GetGeohashes(geometry, child)); | ||
} | ||
catch | ||
{ | ||
// Ignore errors caused by invalid geometry | ||
return Array.Empty<string>(); | ||
} | ||
} | ||
|
||
public static IEnumerable<GeohashTreeNode> GetNextLevel(string geohash = "", Envelope envelope = null) | ||
public GeohashTreeNode? GetTreeNode(string geohash) | ||
{ | ||
GeohashTreeNode? result = null; | ||
foreach (var c in geohash) | ||
{ | ||
if (geohash == string.Empty || envelope == null) | ||
if (c == '-') | ||
{ | ||
geohash = ""; | ||
envelope = new Envelope(-180, 180, -90, 90); | ||
return result; | ||
} | ||
|
||
bool even = geohash.Length % 2 == 0; | ||
var index = Base32.IndexOf(c); | ||
result = result == null ? this[index] : result.GetChildren()[index]; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
return SplitEnvelope2(envelope, even) | ||
.SelectMany(x => SplitEnvelope4(x, even)) | ||
.SelectMany(x => SplitEnvelope4(x, even)) | ||
.Select((envelope1, index) => new GeohashTreeNode { Envelope = envelope1, Geohash = geohash + Base32[index] }); | ||
public static IEnumerable<GeohashTreeNode> GetNextLevel(string geohash = "", Envelope? envelope = null) | ||
{ | ||
if (geohash == string.Empty || envelope == null) | ||
{ | ||
geohash = ""; | ||
envelope = new Envelope(-180, 180, -90, 90); | ||
} | ||
|
||
public static IEnumerable<Envelope> SplitEnvelope2(Envelope envelope, bool even) | ||
var even = geohash.Length % 2 == 0; | ||
|
||
return SplitEnvelope2(envelope, even) | ||
.SelectMany(x => SplitEnvelope4(x, even)) | ||
.SelectMany(x => SplitEnvelope4(x, even)) | ||
.Select((envelope1, index) => new GeohashTreeNode (envelope1, geohash + Base32[index])); | ||
} | ||
|
||
public static IEnumerable<Envelope> SplitEnvelope2(Envelope envelope, bool even) | ||
{ | ||
if (even) | ||
{ | ||
if (even) | ||
{ | ||
|
||
double midX = envelope.MinX + envelope.Width / 2; | ||
yield return new Envelope(envelope.MinX, midX, envelope.MinY, envelope.MaxY); | ||
yield return new Envelope(midX, envelope.MaxX, envelope.MinY, envelope.MaxY); | ||
} | ||
else | ||
{ | ||
double midY = envelope.MinY + envelope.Height / 2; | ||
yield return new Envelope(envelope.MinX, envelope.MaxX, envelope.MinY, midY); | ||
yield return new Envelope(envelope.MinX, envelope.MaxX, midY, envelope.MaxY); | ||
} | ||
var midX = envelope.MinX + envelope.Width / 2; | ||
yield return new Envelope(envelope.MinX, midX, envelope.MinY, envelope.MaxY); | ||
yield return new Envelope(midX, envelope.MaxX, envelope.MinY, envelope.MaxY); | ||
} | ||
|
||
public static IEnumerable<Envelope> SplitEnvelope4(Envelope envelope, bool even) | ||
else | ||
{ | ||
double minX = envelope.MinX; | ||
double minY = envelope.MinY; | ||
var midY = envelope.MinY + envelope.Height / 2; | ||
yield return new Envelope(envelope.MinX, envelope.MaxX, envelope.MinY, midY); | ||
yield return new Envelope(envelope.MinX, envelope.MaxX, midY, envelope.MaxY); | ||
} | ||
} | ||
|
||
public static IEnumerable<Envelope> SplitEnvelope4(Envelope envelope, bool even) | ||
{ | ||
var minX = envelope.MinX; | ||
var minY = envelope.MinY; | ||
|
||
double stepX = envelope.Width / 2; | ||
double stepY = envelope.Height / 2; | ||
var stepX = envelope.Width / 2; | ||
var stepY = envelope.Height / 2; | ||
|
||
if (even) | ||
if (even) | ||
{ | ||
for (var y = 0; y < 2; y++) | ||
{ | ||
for (int y = 0; y < 2; y++) | ||
for (var x = 0; x < 2; x++) | ||
{ | ||
for (int x = 0; x < 2; x++) | ||
{ | ||
double x1 = minX + (stepX * x); | ||
double y1 = minY + (stepY * y); | ||
yield return new Envelope(x1, x1 + stepX, y1, y1 + stepY); | ||
} | ||
var x1 = minX + (stepX * x); | ||
var y1 = minY + (stepY * y); | ||
yield return new Envelope(x1, x1 + stepX, y1, y1 + stepY); | ||
} | ||
} | ||
else | ||
} | ||
else | ||
{ | ||
for (var x = 0; x < 2; x++) | ||
{ | ||
for (int x = 0; x < 2; x++) | ||
for (var y = 0; y < 2; y++) | ||
{ | ||
for (int y = 0; y < 2; y++) | ||
{ | ||
double x1 = minX + (stepX * x); | ||
double y1 = minY + (stepY * y); | ||
yield return new Envelope(x1, x1 + stepX, y1, y1 + stepY); | ||
} | ||
var x1 = minX + (stepX * x); | ||
var y1 = minY + (stepY * y); | ||
yield return new Envelope(x1, x1 + stepX, y1, y1 + stepY); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,23 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using NetTopologySuite.Geometries; | ||
|
||
namespace GeoTimeZone.DataBuilder | ||
namespace GeoTimeZone.DataBuilder; | ||
|
||
public class GeohashTreeNode | ||
{ | ||
public class GeohashTreeNode | ||
private List<GeohashTreeNode>? _children; | ||
private Geometry? _geometry; | ||
|
||
public GeohashTreeNode(Envelope envelope, string geohash) | ||
{ | ||
public string Geohash { get; set; } | ||
Envelope = envelope; | ||
Geohash = geohash; | ||
} | ||
|
||
public Envelope Envelope { get; set; } | ||
public Envelope Envelope { get; } | ||
|
||
private Geometry _geometry; | ||
public Geometry Geometry => _geometry ??= GeometryFactory.Default.ToGeometry(Envelope); | ||
public string Geohash { get; } | ||
|
||
private List<GeohashTreeNode> _children; | ||
public List<GeohashTreeNode> GetChildren() | ||
{ | ||
return _children ??= GeohashTree.GetNextLevel(Geohash, Envelope).ToList(); | ||
} | ||
} | ||
} | ||
public List<GeohashTreeNode> GetChildren() => _children ??= GeohashTree.GetNextLevel(Geohash, Envelope).ToList(); | ||
|
||
public Geometry GetGeometry() => _geometry ??= GeometryFactory.Default.ToGeometry(Envelope); | ||
} |
Oops, something went wrong.