Skip to content

Commit

Permalink
Refactoring and perf improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mattjohnsonpint committed Sep 6, 2022
1 parent 42adc24 commit d87fa78
Show file tree
Hide file tree
Showing 21 changed files with 841 additions and 858 deletions.
18 changes: 18 additions & 0 deletions .editorconfig
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
56 changes: 27 additions & 29 deletions src/GeoTimeZone.DataBuilder/ConsoleOutput.cs
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");
}
}
2 changes: 1 addition & 1 deletion src/GeoTimeZone.DataBuilder/GeoTimeZone.DataBuilder.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
Expand Down
189 changes: 90 additions & 99 deletions src/GeoTimeZone.DataBuilder/GeohashTree.cs
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);
}
}
}
}
}
}
31 changes: 16 additions & 15 deletions src/GeoTimeZone.DataBuilder/GeohashTreeNode.cs
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);
}
Loading

0 comments on commit d87fa78

Please sign in to comment.