using GeoJSON.Net.Geometry; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; using System.Reflection; using System.Windows; using System.Windows.Media; using System.Windows.Shapes; using Point = System.Windows.Point; namespace opdracht2 { public static class GeoJsonParser { public static double maximumXWaarde, maximumYWaarde, minimumXWaarde, minimumYWaarde, schaalXWaarde, schaalYWaarde; public static double epsilon; public static List TriangulateJsonData(string json, double x, double y) { schaalXWaarde = x; schaalYWaarde = y; maximumXWaarde = double.MinValue; minimumXWaarde = double.MaxValue; maximumYWaarde = double.MinValue; minimumYWaarde = double.MaxValue; List returnWaarde = new List(); foreach(JObject v in JObject.Parse(json)["features"]) { try { List EnkelePolygonLijst = new List(); if (v["geometry"]["type"].ToString() == "MultiPolygon") { //TODO nieuwe manier vinden om multipolygons te parsen (dit zijn de geavanceerde polygons die momenteel voor problemen zorgen. MultiPolygon temp = JsonConvert.DeserializeObject(v["geometry"].ToString()); foreach (GeoJSON.Net.Geometry.Polygon geojsonPolygon in temp.Coordinates) { EnkelePolygonLijst = MaakPolygonLijn(MaakPolygonLijst(geojsonPolygon)); returnWaarde.AddRange(maakDriehoeken(EnkelePolygonLijst)); } } else { EnkelePolygonLijst = MaakPolygonLijn(MaakPolygonLijst(JsonConvert.DeserializeObject(v["geometry"].ToString()))); returnWaarde.AddRange(maakDriehoeken(EnkelePolygonLijst)); } Debug.WriteLine("parsed " + v["properties"]["name"]); } catch (Exception e) { Debug.WriteLine("couldn't parse " + v["properties"]["name"] + "reason : " + e.Message); } } return NormalizePolygon(returnWaarde); } private static List> MaakPolygonLijst(GeoJSON.Net.Geometry.Polygon deserializeObject) { double tMaxX = double.MinValue, tMaxY = double.MinValue, tMinX = double.MaxValue, tMinY = double.MaxValue; List> polygonAlsPuntenLijst = new List>(); List puntenLijst = new List(); foreach (LineString lineString in deserializeObject.Coordinates) { foreach (Position positie in lineString.Coordinates) { if (positie.Longitude < tMinX) tMinX = positie.Longitude; if (positie.Longitude > tMaxX) tMaxX = positie.Longitude; if (positie.Latitude < tMinY) tMinY = positie.Latitude; if (positie.Latitude > tMaxY) tMaxY = positie.Latitude; Point punt = new Point(positie.Longitude, positie.Latitude); if (!puntenLijst.Contains(punt)) puntenLijst.Add(new Point( positie.Longitude, positie.Latitude)); } } double nX = tMaxX - tMinX; double nY = tMaxY - tMinY; // versie 1, 1% max verschil lengte punten x OF y double percent = .01; //epsilon = ((nX * percent) < (nY * percent)) ? nX * percent : nY * percent; // versie 2, 1% max verschil gemiddelde lengte x y epsilon = ((nX + nY) / 2) * percent; maximumXWaarde = (maximumXWaarde > tMaxX) ? maximumXWaarde : tMaxX; maximumYWaarde = (maximumYWaarde > tMaxY) ? maximumYWaarde : tMaxY; minimumXWaarde = (minimumXWaarde < tMinX) ? minimumXWaarde : tMinX; minimumYWaarde = (minimumYWaarde < tMinY) ? minimumYWaarde : tMinY; puntenLijst = douglasPeuker(puntenLijst); polygonAlsPuntenLijst.Add(puntenLijst); return polygonAlsPuntenLijst; } private static List maakPunten(List enkelePolygonLijst) { List returnWaarde = new List(); foreach (Point c in enkelePolygonLijst) { Ellipse dot = new Ellipse(); dot.Stroke = Brushes.Black; dot.StrokeThickness = 1; dot.Height = 5; dot.Width = 5; dot.Fill = new SolidColorBrush(Colors.Black); dot.Margin = new Thickness(c.X, c.Y, 0, 0); returnWaarde.Add(dot); } return returnWaarde; } private static List maakDriehoeken(List polygonLijst) { List returnWaarde = new List(); int i = 0; int BACKUP = 0; int BACKBACKUP = polygonLijst.Count; while (true) { if (i >= polygonLijst.Count) { i = 0; if (polygonLijst.Count == BACKBACKUP) { BACKUP++; } BACKBACKUP = polygonLijst.Count; } int punt1Index = i; int punt2Index = i + 1; if (punt2Index >= polygonLijst.Count) punt2Index -= polygonLijst.Count; int punt3Index = i + 2; if (punt3Index >= polygonLijst.Count) punt3Index -= polygonLijst.Count; if (polygonLijst.Count < 3) { break; } double hoek = GetAngle(polygonLijst[punt2Index], polygonLijst[punt1Index], polygonLijst[punt3Index]); if (hoek < 180) { returnWaarde.Add(CreateNewPolygon(polygonLijst[punt2Index], polygonLijst[punt3Index], polygonLijst[punt1Index])); polygonLijst.RemoveAt(punt2Index); Debug.WriteLine("added a triangle, polygonLijst count " + polygonLijst.Count); i = punt1Index; BACKUP = 0; continue; } //Debug.WriteLine(hoek); i++; if (BACKUP >= polygonLijst.Count) { Debug.WriteLine("FUCK, couldnt parse " + polygonLijst.Count + " points"); break; } } return returnWaarde; } //https://stackoverflow.com/a/31334882 private static double GetAngle(Point p1, Point p2, Point p3) { double waarde = (Math.Atan2(p3.Y - p1.Y, p3.X - p1.X) - Math.Atan2(p2.Y - p1.Y, p2.X - p1.X)) * (180 / Math.PI); if (waarde < 0) { waarde += 360; } return waarde; } private static System.Windows.Shapes.Polygon CreateNewPolygon(Point punt1, Point punt2, Point punt3) { System.Windows.Shapes.Polygon returnWaarde = new System.Windows.Shapes.Polygon(); PointCollection puntCollectie = new PointCollection(); puntCollectie.Add(punt1); puntCollectie.Add(punt2); puntCollectie.Add(punt3); returnWaarde.Points = puntCollectie; returnWaarde.StrokeThickness = 1; // deze code zorgt ervoor dat de driehoeken duidelijker zijn op de afbeelding door de kleur willekeurig te selecteren Random random = new Random(); Type brushType = typeof(Brushes); PropertyInfo[] properties = brushType.GetProperties(); int randomIndex = random.Next(properties.Length); Brush willekeurigeBrush = (Brush) properties[randomIndex].GetValue(null, null); returnWaarde.Fill = willekeurigeBrush; returnWaarde.Stroke = willekeurigeBrush; return returnWaarde; } private static List> MaakPolygonLijst(MultiPolygon multiPolygon) { double tMaxX = double.MinValue, tMaxY = double.MinValue, tMinX = double.MaxValue, tMinY = double.MaxValue; List> polygonAlsPuntenLijst = new List>(); foreach (GeoJSON.Net.Geometry.Polygon geojsonPolygon in multiPolygon.Coordinates) { List puntenLijst = new List(); foreach (LineString lineString in geojsonPolygon.Coordinates) { foreach (Position positie in lineString.Coordinates) { if (positie.Longitude < tMinX) { tMinX = positie.Longitude; } if (positie.Longitude > tMaxX) { tMaxX = positie.Longitude; } if (positie.Latitude < tMinY) { tMinY = positie.Latitude; } if (positie.Latitude > tMaxY) { tMaxY = positie.Latitude; } Point punt = new Point(positie.Longitude, positie.Latitude); if (!puntenLijst.Contains(punt)) puntenLijst.Add(punt); } } // TODO gebruik tmax waardes om epsilong te berekenen voor douglasPeuker // door per polygon te berekenen werkt dit ook voor kleinere landen (baseren op grootste afstanden geeft // grote landen meer polygons en kleine minder, nu is de vermindering bij elk gelijk) double nX = tMaxX - tMinX; double nY = tMaxY - tMinY; // versie 1, 1% max verschil lengte punten x OF y double percent = .01; //epsilon = ((nX * percent) < (nY * percent)) ? nX * percent : nY * percent; // versie 2, 1% max verschil gemiddelde lengte x y epsilon = ((nX + nY) / 2) * percent; maximumXWaarde = (maximumXWaarde > tMaxX) ? maximumXWaarde : tMaxX; maximumYWaarde = (maximumYWaarde > tMaxY) ? maximumYWaarde : tMaxY; minimumXWaarde = (minimumXWaarde < tMinX) ? minimumXWaarde : tMinX; minimumYWaarde = (minimumYWaarde < tMinY) ? minimumYWaarde : tMinY; puntenLijst = douglasPeuker(puntenLijst); polygonAlsPuntenLijst.Add(puntenLijst); } polygonAlsPuntenLijst.OrderBy(o => o.Count).Reverse(); return polygonAlsPuntenLijst; } private static List MaakPolygonLijn(List> multiPolygon) { //zoek dichtsbijzijnde punt wanneer toevoegen volgende vector en voeg pas vanaf dat punt toe. bool richting = true; //richting van inlezen polygon (met klok mee, tegen in klok) List returnWaarde = new List(); foreach (List polygon in multiPolygon) { if (richting) { polygon.Reverse(); } List normalizedPoly = polygon; //geen nederlandse naam gevonden voor dit if (returnWaarde.Count > 0) { Point dichtsbijzijndePunt = returnWaarde[0]; Point dichtsbijzijndeNieuwePunt = normalizedPoly[0]; foreach (Point punt in returnWaarde) { //ik dacht dat dit beter ging zijn, maar blijkbaar geen echt effect, behalve shit vertragen. foreach (Point punt2 in normalizedPoly) { if (Vector2.Distance(new Vector2((float)punt.X, (float)punt.Y), new Vector2((float)punt2.X, (float)punt2.Y)) < Vector2.Distance(new Vector2((float) dichtsbijzijndePunt.X,(float) dichtsbijzijndePunt.Y), new Vector2((float)dichtsbijzijndeNieuwePunt.X, (float)dichtsbijzijndeNieuwePunt.Y))) { dichtsbijzijndePunt = punt; dichtsbijzijndeNieuwePunt = punt2; } } } normalizedPoly = orderList(normalizedPoly, dichtsbijzijndeNieuwePunt); returnWaarde.InsertRange(returnWaarde.IndexOf(dichtsbijzijndePunt), normalizedPoly); } else { returnWaarde.AddRange(normalizedPoly); } richting = !richting; } return returnWaarde; } private static List orderList(List normalizedPoly, Point dichtsbijzijndeNieuwePunt) { int i = normalizedPoly.IndexOf(dichtsbijzijndeNieuwePunt); int check = i; List returnWaarde = new List(); do { if (i >= normalizedPoly.Count) i -= normalizedPoly.Count; returnWaarde.Add(normalizedPoly[i]); i++; } while (normalizedPoly.Count != returnWaarde.Count); return returnWaarde; } private static List NormalizePolygon(List polygons) { maximumXWaarde -= minimumXWaarde; maximumYWaarde -= minimumYWaarde; List returnWaarde = new List(); foreach (System.Windows.Shapes.Polygon p in polygons) { PointCollection puntCollectie = new PointCollection(); double schaalWaarde = (schaalXWaarde < schaalYWaarde) ? schaalXWaarde : schaalYWaarde; foreach (Point punt in p.Points) { double x = (punt.X - minimumXWaarde); x /= maximumXWaarde; x *= schaalWaarde; double y = (punt.Y - minimumYWaarde); y /= maximumYWaarde; y *= schaalWaarde; puntCollectie.Add(new Point(x, y)); } returnWaarde.Add(CreateNewPolygon(puntCollectie[0], puntCollectie[1], puntCollectie[2])); } return returnWaarde; } // straight van dp wiki private static List douglasPeuker(List punten) { double dmax = -1; int index = 0; int end = punten.Count; for (int i = 1; i < end-1; i++) { double distance = PerpendicularDistance2(punten[i], punten[0], punten[end-1]); if (distance > dmax) { index = i; dmax = distance; } } List returnWaarde = new List(); if (dmax > epsilon) { List recResults1 = douglasPeuker(punten.GetRange(0, index)); List recResults2 = douglasPeuker(punten.GetRange(index, end-1-index)); returnWaarde.AddRange(recResults1); returnWaarde.AddRange(recResults2); } else { returnWaarde = new List() { punten[0], punten[punten.Count-1] }; } return returnWaarde; } private static double PerpendicularDistance2(Point point, Point l1, Point l2) { return Math.Abs((l2.X - l1.X) * (l1.Y - point.Y) - (l1.X - point.X) * (l2.Y - l1.Y)) / Math.Sqrt(Math.Pow(l2.X - l1.X, 2) + Math.Pow(l2.Y - l1.Y, 2)); } } }