isInside(MkGeoMultiMap country, Vertex point, double borderMargin) { HashSet <string> borders = new HashSet <string>(); HashSet <string> ins = new HashSet <string>(); HashSet <string> outs = new HashSet <string>(); List <(string name, double buffer)> buffers = new List <(string name, double buffer)>(); Dictionary <string, List <(string name, double buffer)> > ret = new Dictionary <string, List <(string name, double buffer)> >(); foreach (KeyValuePair <string, MultiPolygon> kv in country) { string province = kv.Key; MultiPolygon multiPoly = kv.Value; double minBuffer = Double.PositiveInfinity; foreach (Polygon poly in multiPoly) { LinearRing ring = poly.outerRing; var res = isInside(point, ring, borderMargin); if (res.inout == 0) { borders.Add(province); } else if (res.inout == -1) { ins.Add(province); } else if (res.inout == 1) { outs.Add(province); } if (Math.Abs(res.radius) < Math.Abs(minBuffer)) { minBuffer = res.radius; } } if (Math.Abs(minBuffer) < Double.PositiveInfinity) { buffers.Add((name: province, buffer: minBuffer)); } } HashSet <string> prov = new HashSet <string>(ins); prov.UnionWith(borders); foreach (string p in prov) { if (ins.Contains(p)) { ret["in"] = new List <(string name, double buffer)>(); ret["in"].Add((name: p, buffer: 0.0)); } else if (borders.Contains(p)) { if (ret.ContainsKey("border")) { ret["border"].Add((name: p, buffer: 0.0)); } else { ret["border"] = new List <(string name, double buffer)>(); ret["border"].Add((name: p, buffer: 0.0)); } } } if (buffers.Count > 0) { ret["buffer"] = buffers; } Dictionary <string, (string name, double buffer)[]> retn = new Dictionary <string, (string name, double buffer)[]>();
void parseGeoJson(JObject jobj) { string jtype = ""; JArray features; try { jtype = jobj["type"].Value <string>(); if (!jtype.Equals("FeatureCollection")) { new Exception("Invalid geo type: " + jtype); } features = (JArray)jobj["features"]; foreach (JObject feature in features) { string ftype = feature["type"].Value <string>(); if (!ftype.Equals("Feature")) { new Exception("Invalid feature type: " + ftype); } // feature["properties"] = { // "name": "Quebec", // "cartodb_id": 1, // "created_at": "2014-10-16T13:22:00Z", // "updated_at": "2014-10-16T13:22:00Z" // } string name = ((JObject)feature["properties"])["name"].Value <string>(); JObject geometry = (JObject)feature["geometry"]; string gtype = geometry["type"].Value <string>(); JArray coords = (JArray)geometry["coordinates"]; JArray polyCoords = null; if (gtype.Equals("MultiPolygon")) { polyCoords = coords; } else if (gtype.Equals("Polygon")) { polyCoords = new JArray(); polyCoords.Add(coords); } else { throw new Exception("Unsurported geometry type"); } Polygon[] polys = new Polygon[polyCoords.Count]; for (int p = 0; p < polyCoords.Count; ++p) { JArray coords1 = (JArray)polyCoords[p]; LinearRing[] rings = new LinearRing[coords1.Count]; for (int r = 0; r < coords1.Count; ++r) { JArray coords2 = (JArray)coords1[r]; Vertex[] verts = new Vertex[coords2.Count]; for (int v = 0; v < coords2.Count; ++v) { JArray vertex = (JArray)coords2[v]; verts[v] = new Vertex(vertex[0].Value <double>(), vertex[1].Value <double>()); } rings[r] = new LinearRing(verts); } polys[p] = new Polygon(rings); } this[name] = new MultiPolygon(polys); } } catch (Exception exc) { Console.WriteLine("Exception: " + exc.Message); throw exc; } }
isInside(Vertex point, LinearRing outerRing, double radiusOfInterest) { if (DEBUG) { if (radiusOfInterest < 0.0) { throw new Exception("MkGeoAlgo.isInside: radiusOfInterest must be greater than 0"); } } // test the bounding box of the ring with radiusOfInterest var bb = outerRing.boundingBox(); if (!isInsideBoundingBox(bb.bottomLeft, bb.topRight, point, radiusOfInterest)) { return(1, nextFloat(radiusOfInterest)); } double bbScale = maxAbs(bb.bottomLeft, bb.topRight); if (bbScale < 1.0) { bbScale = 1.0; } double epsilon = nextFloat(bbScale) - bbScale; int effectiveLength = outerRing.length - 1; Func <int, int> roundIndex = n => (n + effectiveLength) % effectiveLength; EdgeBBTester edgeBBTester = new EdgeBBTester(point, radiusOfInterest); int numIntersection = 0; int inout = Int32.MaxValue; // should be -1 (inside), 0 (boundary), 1 (outside) double buffer = radiusOfInterest; // Determine the initial state. We wlll loop through the verts from "bottom" // We will start the loop from bottom // bottom is the index of the lowest vert int bottom = outerRing.bottomVert; Vertex v1 = outerRing[bottom]; RayState state = RayState.Below; if (v1.y < point.y) { state = RayState.Below; } else if (v1.y > point.y) { state = RayState.RayDone; inout = 1; // in the following v1.y === point.y } else if (bb.bottomLeft.y == bb.topRight.y) { // bounding box degenerates to horizontal line segment inout = 0; state = RayState.RayDone; } else { // Find the left-most bottom int j = bottom; for (int i = 1; i < outerRing.length; ++i) { int k = roundIndex(bottom - i); Vertex v_k = outerRing[k]; // notice that v1.y is least among all verts // we are sure to find v0 if (v_k.y > v1.y) { break; } else { j = k; } } bottom = j; state = RayState.AboveToMiddle; } // Assertion: state === RayDone || inout == Int32.MaxValue // state !== RayDone || inout != Int32.MaxValue Vertex v2 = outerRing[bottom]; Vertex v0; Vertex xmin, xmax, ymin, ymax; v1 = outerRing[roundIndex(bottom - 1)]; for (int i = 1; i < outerRing.length; ++i) { v0 = v1; v1 = v2; v2 = outerRing[roundIndex(bottom + i)]; if (v1.x < v2.x) { xmin = v1; xmax = v2; } else { xmin = v2; xmax = v1; } if (v1.y < v2.y) { ymin = v1; ymax = v2; } else { ymin = v2; ymax = v1; } if (state == RayState.RayDone) { if (buffer == 0.0) { break; } else { // We have to test edges for the distance if (edgeBBTester.isInBoundingBox(xmin, xmax, ymin, ymax)) { double updated = updateBorderBuffer(buffer, v0, v1, v2, point, epsilon); // Assertion: updated == buffer || |updated| < |buffer| && |updated| < radiusOfInterest if (Math.Abs(updated) < Math.Abs(buffer)) { edgeBBTester.tightenOffset(Math.Abs(updated)); buffer = updated; } } continue; } } else if (state == RayState.Below || state == RayState.Above) { if (point.y == v2.y) { // entering Middle if (point.x == v2.x) { inout = 0; state = RayState.RayDone; } else { state = (state == RayState.Below) ? RayState.BelowToMiddle : RayState.AboveToMiddle; } } else if (state == RayState.Below ? v2.y > point.y : v2.y < point.y) { // Below -> Above || Above -> Below state = state == RayState.Below ? RayState.Above : RayState.Below; if (point.x <= xmax.x) { if (point.x <= xmin.x) { // there must be hit numIntersection += 1; } else { // xmin.x < point.x <= xmax.x double d = signedDist(ymin, ymax, point); if (d == 0.0) { // hit the boundary inout = 0; state = RayState.RayDone; } else if (d > 0.0) { // one hit numIntersection += 1; } // otherwise, Δ < 0: there is no hit } } else { // point.x > xmax.x: there is no hit // if debug // println("Test 8.5") // end } } // otherwise, state === Below ? v2.y < point.y : v2.y > point.y, and we remain in Below/Above } else if (state == RayState.BelowToMiddle || state == RayState.AboveToMiddle) { // We are on the Middle line if (point.y == v2.y) { // Stay on the Middle if (xmin.x <= point.x && point.x <= xmax.x) { // we are on the horizontal edge inout = 0; state = RayState.RayDone; } } else if (state == RayState.BelowToMiddle ? v2.y <point.y : v2.y> point.y) { // Below -> BelowToMiddle -> Below // or, Above -> AboveToMiddle -> Above state = state == RayState.BelowToMiddle ? RayState.Below : RayState.Above; } else { // state === BelowToMiddle ? v2.y > point.y : v2.y < point.y // Below -> BelowToMiddle -> Above // or, Above -> AboveToMiddle -> Below state = state == RayState.BelowToMiddle ? RayState.Above : RayState.Below; if (point.x <= v1.x) { numIntersection += 1; } } } // end of state transition } // end of the loop through all the verts // println("number of intersection = " * string(numIntersection)) if (inout == Int32.MaxValue) { if (numIntersection % 2 == 0) { inout = 1; } else { inout = -1; } } if (buffer == radiusOfInterest) { if (radiusOfInterest >= Double.MaxValue) { string err = "logical error: the distance to the border cannot be infinity"; if (DEBUG) { throw new Exception(err); } else if (geoFenceLogger != null) { geoFenceLogger(err); } } buffer = inout < 0 ? -nextFloat(radiusOfInterest) : nextFloat(radiusOfInterest); } return(inout, buffer); } // isInside(LinearRing )