public async Task <bool> ExportResults(IEnumerable <Hexagon> hexagons, HexagonDefinition hexagonDefinition, MergeStrategy mergeStrategy) { foreach (Hexagon hexagon in hexagons) { string insertCommand = @"INSERT INTO HexagonLocation (U,V,PIXELX,PIXELY) VALUES (@U,@V,@PixelX,@PixelY) ON CONFLICT(U,V) DO UPDATE SET PixelX=excluded.PixelX,PixelY=excluded.PixelY"; PointXY hexagonCenter = HexagonUtils.GetCenterPointXYOfHexagonLocationUV(hexagon.LocationUV, hexagonDefinition); await _connection.ExecuteAsync(insertCommand, new { hexagon.LocationUV.U, hexagon.LocationUV.V, PixelX = hexagonCenter.X, PixelY = hexagonCenter.Y }); foreach (var key in hexagon.HexagonData.Data.Keys) { object dataValue = hexagon.HexagonData.Data[key]; string insertDataCommand = @"INSERT INTO HexagonData (U,V,FIELD,VALUE) VALUES (@U,@V,@Field,@Value) ON CONFLICT(U,V,FIELD) DO "; switch (mergeStrategy) { case MergeStrategy.BitMask: insertDataCommand += "UPDATE SET VALUE=VALUE|excluded.Value"; break; case MergeStrategy.Ignore: insertDataCommand += "NOTHING"; break; case MergeStrategy.Replace: insertDataCommand += "UPDATE Set Value=excluded.Value"; break; case MergeStrategy.Max: insertDataCommand += "UPDATE Set Value=max(excluded.Value,Value)"; break; case MergeStrategy.Min: insertDataCommand += "UPDATE Set Value=min(excluded.Value,Value)"; break; } await _connection.ExecuteAsync(insertDataCommand, new { hexagon.LocationUV.U, hexagon.LocationUV.V, Field = key, Value = dataValue }); } } return(true); }
/// <summary> /// </summary> /// <param name="source"></param> /// <param name="destination"></param> /// <param name="hexagonDefinition"></param> /// <returns></returns> public static int GetDistanceBetweenHexagonLocationUVs(HexagonLocationUV source, HexagonLocationUV destination, HexagonDefinition hexagonDefinition) { var du = destination.U - source.U; var dv = destination.V - source.V; return(du * dv > 0 ? Math.Abs(du + dv) : Math.Max(Math.Abs(du), Math.Abs(dv))); }
/// <summary> /// Obtains the center point (x,y) of an hexagon given its LocationUv (u,v) /// </summary> /// <param name="location"></param> /// <param name="hexagonDefinition"></param> /// <returns></returns> public static PointXY GetCenterPointXYOfHexagonLocationUV(HexagonLocationUV location, HexagonDefinition hexagonDefinition) { var x = hexagonDefinition.NarrowWidth * location.U; var y = hexagonDefinition.Height * (location.U * 0.5 + location.V); return(new PointXY(x, y)); }
public string GenerateMap(List <object> hexagons, HexagonDefinition hexagonDefinition, int hexagonReferenceZoom) { GeoJsonWriter writer = new GeoJsonWriter(); FeatureCollection hexagonCollection = new FeatureCollection(); foreach (IDictionary <string, object> hexagon in hexagons) { HexagonLocationUV locationUV = new HexagonLocationUV(Convert.ToInt32(hexagon["U"]), Convert.ToInt32(hexagon["V"])); IList <PointXY> points = HexagonUtils.GetPointsXYOfHexagon(locationUV, hexagonDefinition); LinearRing ring = new LinearRing(points.Select(pixelCoordinate => { var(latitude, longitude) = TileSystem.PixelXYToLatLong((int)pixelCoordinate.X, (int)pixelCoordinate.Y, hexagonReferenceZoom); return(new Coordinate(longitude, latitude)); }).ToArray()); Polygon hexagonPolygon = new Polygon(ring); AttributesTable attributes = new AttributesTable( new List <KeyValuePair <string, object> > { new KeyValuePair <string, object>("U", locationUV.U), new KeyValuePair <string, object>("V", locationUV.V), }); foreach (var key in hexagon.Keys) { switch (key) { case "U": case "V": break; default: object value = hexagon[key]; attributes.Add(key, value); break; } } Feature hexagonFeature = new Feature(hexagonPolygon, attributes); hexagonCollection.Add(hexagonFeature); } //4. Export Geojson just for the hexagons in this particular tile string result = writer.Write(hexagonCollection); return(result); }
public Task <bool> ExportResults(IEnumerable <Hexagon> hexagons, HexagonDefinition hexagonDefinition, MergeStrategy mergeStrategy) { foreach (var hexagon in hexagons) { foreach (var key in hexagon.HexagonData.Data.Keys) { Console.WriteLine($"({hexagon.LocationUV.U}:{hexagon.LocationUV.U}) {key}:{hexagon.HexagonData[key]}"); } } return(Task.FromResult(true)); }
/// <summary> /// Determines if a specified point (x,y) is inside a given hexagon Location (u,v) /// </summary> /// <param name="point"></param> /// <param name="location"></param> /// <param name="hexagonDefinition"></param> /// <returns>True if inside the hexagon, false otherwise</returns> public static bool IsPointXYInsideHexagonLocationUV(PointXY point, HexagonLocationUV location, HexagonDefinition hexagonDefinition) { var center = GetCenterPointXYOfHexagonLocationUV(location, hexagonDefinition); var d = hexagonDefinition.Diameter; var dx = Math.Abs(point.X - center.X) / d; var dy = Math.Abs(point.Y - center.Y) / d; var a = 0.25 * Math.Sqrt(3.0); return(dy <= a && a * dx + 0.25 * dy <= 0.5 * a); }
public IActionResult Get(int z, int x, int y) { using (var destinationImage = new Image <Rgba32>(TileSize, TileSize)) { if (z >= 7) { var hexSize = 500; var size = (int)(hexSize / Math.Pow(2, 10 - z)); var hexagonDefinition = new HexagonDefinition(size, 10); //Tile offset var pixelX = x * TileSize; var pixelY = y * TileSize; var topLeft = new PointXY(pixelX - size, pixelY - size); var bottomRight = new PointXY(pixelX + TileSize + size, pixelY + TileSize + size); foreach (HexagonLocationUV hexagon in HexagonUtils.GetHexagonsInsideBoundingBox(topLeft, bottomRight, hexagonDefinition)) { PointXY center = HexagonUtils.GetCenterPointXYOfHexagonLocationUV(hexagon, hexagonDefinition); IList <PointXY> hexagonPoints = HexagonUtils .GetHexagonPixels(size, new PointXY(center.X - pixelX, center.Y - pixelY)).ToList(); PointF[] points = hexagonPoints.Select(p => new PointF((float)p.X, (float)p.Y)).ToArray(); destinationImage.Mutate(ctx => ctx .DrawLines( new Rgba32(200, 200, 200, 200), 5, points) .DrawLines( new Rgba32(100, 100, 100, 200), 2, points)); } } Stream outputStream = new MemoryStream(); destinationImage.Save(outputStream, new PngEncoder()); outputStream.Seek(0, SeekOrigin.Begin); return(this.File(outputStream, "image/png")); } }
internal static void Process(GeoDataToHexagonDataArgs opts) { Stopwatch stopwatch = Stopwatch.StartNew(); LayersConfiguration layer = opts.GetLayersConfiguration(); MergeStrategy mergeStrategy = opts.Merge; ValueHandlerFactory valueHandlerFactory = new ValueHandlerFactory(); valueHandlerFactory.RegisterImplementation <WikimediaAltitudeHandler>("wikimedia_altitude"); HexagonDefinition hexagonDefinition = opts.GetHexagonDefinition(); HexagonProcessor hexagonProcessor = new HexagonProcessor(hexagonDefinition, valueHandlerFactory); BoundingBox bb = null; if (!string.IsNullOrEmpty(opts.East) && !string.IsNullOrEmpty(opts.West) && !string.IsNullOrEmpty(opts.North) && !string.IsNullOrEmpty(opts.South)) { bb = new BoundingBox { East = double.Parse(opts.East), West = double.Parse(opts.West), North = double.Parse(opts.North), South = double.Parse(opts.South) }; } using (IGeoDataParser geoParser = opts.GetGeoDataParser()) using (IHexagonDataExporter exporter = opts.GetResultExporter()) { IEnumerable <GeoData> geoDataList = geoParser.ParseGeodataFromSource(layer); IEnumerable <Hexagon> results = hexagonProcessor.ProcessHexagonsFromGeoData(geoDataList, layer.Targets, bb); ExportResults(exporter, results, hexagonDefinition, mergeStrategy); } stopwatch.Stop(); Console.WriteLine($"Process took {stopwatch.ElapsedMilliseconds} ms"); }
public static IEnumerable <TileInfo> GetTilesContainingHexagon(Hexagon hexagon, int minZoomLevel, int maxZoomLevel, HexagonDefinition hexagonDefinition, int tileSize) { var tiles = new HashSet <TileInfo>(); var points = GetPointsXYOfHexagon(hexagon.LocationUV, hexagonDefinition); var topLeft = new PointXY(points[0].X, points[1].Y); var topRight = new PointXY(points[3].X, points[1].Y); var bottomLeft = new PointXY(points[0].X, points[4].Y); var bottomRight = new PointXY(points[3].X, points[4].Y); for (var zoomLevel = minZoomLevel; zoomLevel <= maxZoomLevel; zoomLevel++) { tiles.Add(GetTileOfPoint(topLeft, zoomLevel, tileSize, hexagonDefinition)); tiles.Add(GetTileOfPoint(topRight, zoomLevel, tileSize, hexagonDefinition)); tiles.Add(GetTileOfPoint(bottomLeft, zoomLevel, tileSize, hexagonDefinition)); tiles.Add(GetTileOfPoint(bottomRight, zoomLevel, tileSize, hexagonDefinition)); } return(tiles); }
public static IEnumerable <HexagonLocationUV> GetHexagonsInsideBoundingBox(PointXY topLeftCorner, PointXY bottomRightCorner, HexagonDefinition hexagonDefinition) { var topLeftUV = GetHexagonLocationUVForPointXY(topLeftCorner, hexagonDefinition); var bottomLeftUV = GetHexagonLocationUVForPointXY(new PointXY(topLeftCorner.X, bottomRightCorner.Y), hexagonDefinition); var bottomRightUV = GetHexagonLocationUVForPointXY(bottomRightCorner, hexagonDefinition); var rowsEven = bottomLeftUV.V - topLeftUV.V + 1; var topLeftUVCenter = GetCenterPointXYOfHexagonLocationUV(topLeftUV, hexagonDefinition); var minX2 = topLeftUVCenter.X + hexagonDefinition.NarrowWidth; var topLeftUV2 = GetHexagonLocationUVForPointXY(new PointXY(minX2, topLeftCorner.Y), hexagonDefinition); var bottomLeftUV2 = GetHexagonLocationUVForPointXY(new PointXY(minX2, bottomRightCorner.Y), hexagonDefinition); var rowsOdd = bottomLeftUV2.V - topLeftUV2.V + 1; var columns = bottomRightUV.U - topLeftUV.U + 1; for (var i = 0; i < columns; i++) { var rows = i % 2 == 0 ? rowsEven : rowsOdd; for (var j = 0; j < rows; j++) { var offset = topLeftUV.V == topLeftUV2.V ? Convert.ToInt32(Math.Floor((double)i / 2)) : Convert.ToInt32(Math.Ceiling((double)i / 2)); var u = topLeftUV.U + i; var v = topLeftUV.V + j - offset; yield return(new HexagonLocationUV(u, v)); } } }
internal static void Process(HexagonDataToMapArgs opts) { Stopwatch stopwatch = Stopwatch.StartNew(); HexagonDefinition hexagonDefinition = opts.GetHexagonDefinition(); //1.1. Get Bounding box coordinates BoundingBox bb = null; if (!string.IsNullOrEmpty(opts.Tile)) { string[] tileSegments = opts.Tile.Split(":"); if (tileSegments.Length != 3) { throw new NotSupportedException($"A tile format must be supplied as z:x:y"); } int zoom = int.Parse(tileSegments[0]); int tileX = int.Parse(tileSegments[1]); int tileY = int.Parse(tileSegments[2]); var(pixelXLeft, pixelYTop) = TileSystem.TileXYToPixelXY(tileX, tileY); var(n, w) = TileSystem.PixelXYToLatLong(pixelXLeft, pixelYTop, opts.HexagonReferenceZoom); var(pixelXRight, pixelYBottom) = TileSystem.TileXYToPixelXY(tileX + 1, tileY + 1); var(s, e) = TileSystem.PixelXYToLatLong(pixelXRight, pixelYBottom, opts.HexagonReferenceZoom); bb = new BoundingBox { East = e, West = w, North = n, South = s }; } else if (!string.IsNullOrEmpty(opts.East) && !string.IsNullOrEmpty(opts.West) && !string.IsNullOrEmpty(opts.North) && !string.IsNullOrEmpty(opts.South)) { bb = new BoundingBox { East = double.Parse(opts.East), West = double.Parse(opts.West), North = double.Parse(opts.North), South = double.Parse(opts.South) }; } List <object> hexagons; var hexagonDataReader = new SQLiteHexagonReader(opts.Input); if (bb == null) { hexagons = hexagonDataReader.Read(opts.DataProperties.ToArray()).ToList(); } else { var(left, top) = TileSystem.LatLongToPixelXY(bb.North, bb.West, opts.HexagonReferenceZoom); var(right, bottom) = TileSystem.LatLongToPixelXY(bb.South, bb.East, opts.HexagonReferenceZoom); hexagons = hexagonDataReader.Read(left, right, top, bottom, opts.DataProperties.ToArray()).ToList(); } using (StreamWriter outfile = new StreamWriter(opts.Output)) { GeojsonExporter exporter = new GeojsonExporter(); var result = exporter.GenerateMap(hexagons, hexagonDefinition, opts.HexagonReferenceZoom); outfile.Write(result); } stopwatch.Stop(); Console.WriteLine($"Process took {stopwatch.ElapsedMilliseconds} ms"); }
private static void ExportResults(IHexagonDataExporter exporter, IEnumerable <Hexagon> results, HexagonDefinition hexagonDefinition, MergeStrategy mergeStrategy) { exporter.ExportResults(results, hexagonDefinition, mergeStrategy).Wait(); }
public HexagonProcessor(HexagonDefinition hexagonDefinition, ValueHandlerFactory valueHandlerFactory = null) { this.hexagonDefinition = hexagonDefinition; this.valueHandlerFactory = valueHandlerFactory; }
public static IList <PointXY> GetPointsXYOfHexagon(HexagonLocationUV location, HexagonDefinition hexagonDefinition) { var center = GetCenterPointXYOfHexagonLocationUV(location, hexagonDefinition); return(new List <PointXY> { new PointXY(center.X - hexagonDefinition.Diameter / 2.0, center.Y), new PointXY(center.X - hexagonDefinition.EdgeSize / 2.0, center.Y - hexagonDefinition.Height / 2.0), new PointXY(center.X + hexagonDefinition.EdgeSize / 2.0, center.Y - hexagonDefinition.Height / 2.0), new PointXY(center.X + hexagonDefinition.Diameter / 2.0, center.Y), new PointXY(center.X + hexagonDefinition.EdgeSize / 2.0, center.Y + hexagonDefinition.Height / 2.0), new PointXY(center.X - hexagonDefinition.EdgeSize / 2.0, center.Y + hexagonDefinition.Height / 2.0), new PointXY(center.X - hexagonDefinition.Diameter / 2.0, center.Y) }); }
/// <summary> /// Obtains the Hexagon Location (u,v) for a given point (x,y) /// </summary> /// <remarks> /// This algorithm follows a two step approach. The first one is very first and although doesn't have false positives /// might have false negatives. /// Thus, on a second iteration (if the first didn't return true) it checks the vicinity hexagons and uses a more /// deterministic polygon intersection mechanism. /// </remarks> /// <param name="point">A given X,Y point</param> /// <param name="hexagonDefinition"></param> /// aaa /// <returns>An hexagon U,V Location</returns> public static HexagonLocationUV GetHexagonLocationUVForPointXY(PointXY point, HexagonDefinition hexagonDefinition) { var u = Convert.ToInt32(Math.Round(point.X / hexagonDefinition.NarrowWidth)); var v = Convert.ToInt32(Math.Round(point.Y / hexagonDefinition.Height - u * 0.5)); if (IsPointXYInsideHexagonLocationUV(point, new HexagonLocationUV(u, v), hexagonDefinition)) { return(new HexagonLocationUV(u, v)); } var surroundingHexagons = new List <HexagonLocationUV> { new HexagonLocationUV(u, v - 1), new HexagonLocationUV(u, v + 1), new HexagonLocationUV(u - 1, v), new HexagonLocationUV(u + 1, v), new HexagonLocationUV(u - 1, v + 1), new HexagonLocationUV(u + 1, v - 1) }; foreach (var hex in surroundingHexagons) { if (IsPointXYInsideHexagonLocationUV(point, hex, hexagonDefinition)) { return(hex); } } return(null); }
private static TileInfo GetTileOfPoint(PointXY pointXY, int zoomLevel, int tileSize, HexagonDefinition hexagonDefinition) { var pixelFactor = Math.Pow(2, zoomLevel - hexagonDefinition.ReferenceZoom); var tileX = Convert.ToInt32(Math.Floor(pointXY.X * pixelFactor / tileSize)); var tileY = Convert.ToInt32(Math.Floor(pointXY.Y * pixelFactor / tileSize)); return(new TileInfo(zoomLevel, tileX, tileY)); }
public static IEnumerable <HexagonLocationUV> GetHexagonsIntersectedByLine(PointXY startPoint, PointXY endPoint, HexagonDefinition hexagonDefinition) { var startHexagon = HexagonUtils.GetHexagonLocationUVForPointXY(startPoint, hexagonDefinition); var endHexagon = HexagonUtils.GetHexagonLocationUVForPointXY(endPoint, hexagonDefinition); var minU = new[] { startHexagon.U, endHexagon.U }.Min() - 1; var maxU = new[] { startHexagon.U, endHexagon.U }.Max() + 1; var minV = new[] { startHexagon.V, endHexagon.V }.Min() - 1; var maxV = new[] { startHexagon.V, endHexagon.V }.Max() + 1; var matchingHexagons = new List <HexagonLocationUV>(); for (var u = minU; u <= maxU; u++) { for (var v = minV; v <= maxV; v++) { var points = HexagonUtils.GetPointsXYOfHexagon(new HexagonLocationUV(u, v), hexagonDefinition); for (var k = 0; k < points.Count() - 1; k++) { var p0 = points[k]; var p1 = points[k + 1]; if (GetLineIntersection(p0, p1, startPoint, endPoint) != null) { matchingHexagons.Add(new HexagonLocationUV(u, v)); break; } } } } return(matchingHexagons); }