Find Intersection Point Between 2 LineStrings - google-maps

I created a formula to form a grid on the google earth. I want to get the intersection point between the lat/long. Please tell me how we can get the intersection.I am using SharpKML lib for generating KML
for (int x = 90; x >= 0; x = x - 15)
{
Placemark placemark = new Placemark();
LineString line = new LineString();
CoordinateCollection co = new CoordinateCollection();
for (int i = 0; i <= 180; i = i + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
for (int i = -180; i <= 0; i = i + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
line.Coordinates = co;
placemark.Geometry = line;
document.AddFeature(placemark);
}
for (int x = -90; x <= 0; x = x + 15)
{
Placemark placemark = new Placemark();
LineString line = new LineString();
CoordinateCollection co = new CoordinateCollection();
for (int i = 0; i <= 180; i = i + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
for (int i = -180; i <= 0; i = i + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
line.Coordinates = co;
placemark.Geometry = line;
document.AddFeature(placemark);
}
for (int i = 0; i <= 180; i = i + 15)
{
Placemark placemark = new Placemark();
LineString line = new LineString();
CoordinateCollection co = new CoordinateCollection();
for (int x = 0; x <= 90; x = x + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
for (int x = -90; x <= 0; x = x + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
line.Coordinates = co;
placemark.Geometry = line;
document.AddFeature(placemark);
}
for (int i = -180; i <= 0; i = i + 15)
{
Placemark placemark = new Placemark();
LineString line = new LineString();
CoordinateCollection co = new CoordinateCollection();
for (int x = 0; x <= 90; x = x + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
for (int x = -90; x <= 0; x = x + 15)
{
Vector cords = new Vector()
{
Latitude = x,
Longitude = i,
Altitude = 1000
};
co.Add(cords);
}
line.Coordinates = co;
placemark.Geometry = line;
document.AddFeature(placemark);
}

Matthew is correct if the question is how you can find the intersection point of an arbitrary LineString object with your grid using C#. In C++ you can use GEOS http://trac.osgeo.org/geos/ in Java it would be JTS http://www.vividsolutions.com/jts/JTSHome.htm.
If, however, you are creating the grid yourself, and want an answer to the far simpler question of how do I find the intersection points between the horizontal and vertical lines of the grid I just created, the answer would be to use the same exact latitude, longitude values that you used for the LineStrings in a nested loop:
Document document = new Document();
for(y = -90; y < 0; y += 15){
for(x = -180; x < 0; x+= 15){
Point point = new Point();
point.Coordinate = new Vector(x, y);
Placemark placemark = new Placemark();
placemark.Geometry = point;
document.AddFeature(placemark);
}
}
.. repeat for the other 4 quadrants
// It's conventional for the root element to be Kml,
// but you could use document instead.
Kml root = new Kml();
root.Feature = document;
XmlFile kml = KmlFile.Create(root, false);
Here is some source code if you wanted to use DotSpatial, for instance, to find the intersection between the grids and a Shapefile. In this case, the shapefile has river lines and only produced one intersection point. Be advised that the topology intersection code is kind of slow, so you will want to use extent checking to speed things up. In your case you may want to build new Features by using KMLSharp to read a the linestring coordinates in a kml source file, rather than opening a shapefile, but the intersection code would be similar.
As a side note, I don't think the seemingly easy to use FeatureSet.Intersection method is smart enough to handle the case where line intersections produce point features as intersections. It only works for points or polygons where the output is likely to be the same feature type as the input.
using DotSpatial.Controls;
using DotSpatial.Data;
using DotSpatial.Topology;
using DotSpatial.Symbology;
private FeatureSet gridLines;
private void buttonAddGrid_Click(object sender, EventArgs e)
{
gridLines = new FeatureSet(FeatureType.Line);
for (int x = -180; x < 0; x += 15)
{
List<Coordinate> coords = new List<Coordinate>();
coords.Add(new Coordinate(x, -90));
coords.Add(new Coordinate(x, 90));
LineString ls = new LineString(coords);
gridLines.AddFeature(ls);
}
for (int y = -90; y < 0; y += 15)
{
List<Coordinate> coords = new List<Coordinate>();
coords.Add(new Coordinate(-180, y));
coords.Add(new Coordinate(180, y));
LineString ls = new LineString(coords);
gridLines.AddFeature(ls);
}
map1.Layers.Add(new MapLineLayer(gridLines));
}
private void buttonIntersect_Click(object sender, EventArgs e)
{
if (gridLines == null)
{
MessageBox.Show("First add the grid.");
}
IFeatureSet river = FeatureSet.Open(#"C:\Data\Rivers\River.shp");
MapLineLayer riverLayer = new MapLineLayer(river);
map1.Layers.Add(river);
List<DotSpatial.Topology.Point> allResultPoints = new List<DotSpatial.Topology.Point>();
foreach (Feature polygon in river.Features)
{
Geometry lineString = polygon.BasicGeometry as Geometry;
foreach (Feature lineFeature in gridLines.Features)
{
// Speed up calculation with extent testing.
if(!lineFeature.Envelope.Intersects(lineString.Envelope)){
continue;
}
IFeature intersectFeature = lineFeature.Intersection(lineString);
if (intersectFeature == null)
{
continue;
}
MultiPoint multi = intersectFeature.BasicGeometry as MultiPoint;
if (multi != null)
{
for(int i = 0; i < multi.NumGeometries; i++)
{
allResultPoints.Add(intersectFeature.GetBasicGeometryN(i) as DotSpatial.Topology.Point);
}
}
DotSpatial.Topology.Point single = intersectFeature.BasicGeometry as DotSpatial.Topology.Point;
{
allResultPoints.Add(single);
}
}
}
FeatureSet finalPoints = new FeatureSet(FeatureType.Point);
foreach(DotSpatial.Topology.Point pt in allResultPoints){
finalPoints.AddFeature(pt);
}
map1.Layers.Add(new MapPointLayer(finalPoints));
}

I think the DotSpatial library should meet your needs, I have used this library in the past but not made use of the intersections function:
http://dotspatial.codeplex.com/wikipage?title=DotSpatial.Data.FeatureSetExt.Intersection
If you try and do your own line intersection analysis, know that a simplistic Cartesian plane approach will introduce errors (which [I think] become more obvious as you approach the poles).
See here: http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
And here: Intersection between two geographic lines

Related

google maps flutter check if a point inside a polygon

I'm working on flutter project using google-maps-flutter plugin, and I want to check if the user location is inside the polygon that I created on the map. There is an easy way using JavaScript api (containsLocation() method) but for flutter I only found a third party plugin,google_map_polyutil, which is only for android and I get a security worming when I run my app. Is there another way to do so??
I found this answer and just modified some minor things to work with dart, I ran a test on a hardcoded polygon. The list _area is my polygon and _polygons is required for my mapcontroller.
final Set<Polygon> _polygons = {};
List<LatLng> _area = [
LatLng(-17.770992200, -63.207739700),
LatLng(-17.776386600, -63.213576200),
LatLng(-17.778348200, -63.213576200),
LatLng(-17.786848100, -63.214262900),
LatLng(-17.798289700, -63.211001300),
LatLng(-17.810547700, -63.200701600),
LatLng(-17.815450600, -63.185252100),
LatLng(-17.816267800, -63.170660900),
LatLng(-17.800741300, -63.153838100),
LatLng(-17.785867400, -63.150919800),
LatLng(-17.770501800, -63.152636400),
LatLng(-17.759712400, -63.160361200),
LatLng(-17.755952300, -63.169802600),
LatLng(-17.752519100, -63.186625400),
LatLng(-17.758404500, -63.195551800),
LatLng(-17.770992200, -63.206538100),
LatLng(-17.770996000, -63.207762500)];
The function ended like this:
bool _checkIfValidMarker(LatLng tap, List<LatLng> vertices) {
int intersectCount = 0;
for (int j = 0; j < vertices.length - 1; j++) {
if (rayCastIntersect(tap, vertices[j], vertices[j + 1])) {
intersectCount++;
}
}
return ((intersectCount % 2) == 1); // odd = inside, even = outside;
}
bool rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if ((aY > pY && bY > pY) || (aY < pY && bY < pY) || (aX < pX && bX < pX)) {
return false; // a and b can't both be above or below pt.y, and a or
// b must be east of pt.x
}
double m = (aY - bY) / (aX - bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
Notice the polygons property and the onTap method. I was trying to check if the marker created in my map was inside my polygon:
GoogleMap(
initialCameraPosition: CameraPosition(
target: target, //LatLng(0, 0),
zoom: 16,
),
zoomGesturesEnabled: true,
markers: markers,
polygons: _polygons,
onMapCreated: (controller) =>
_mapController = controller,
onTap: (latLng) {
_getAddress(latLng);
},
)
Then i just used the following call in my _getAddress method:
_checkIfValidMarker(latLng, _area);
I hope it helps you to create what you need.
The easiest way to use it - https://pub.dev/packages/maps_toolkit
with isLocationOnPath method.
L. Chi's answer really help.
But due to I have pretty close points, rayCastIntersect might have wrong boolean return if aX is equal to bX
Therefore, I just add aX == bX condition check before calculate m then it works.
bool rayCastIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if ((aY > pY && bY > pY) || (aY < pY && bY < pY) || (aX < pX && bX < pX)) {
return false; // a and b can't both be above or below pt.y, and a or
// b must be east of pt.x
}
if (aX == bX) {
return true;
}
double m = (aY - bY) / (aX - bX); // Rise over run
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m; // algebra is neat!
return x > pX;
}
The easiest way to use it - https://pub.dev/packages/maps_toolkit
with PolygonUtil.containsLocation - computes whether the given point lies inside the specified polygon.

Google Maps API Snap Marker To Polyline

I have a polyline list (from Google Directions API) that has been drawn on Google Map. Next, I'd like for my marker to move along this line without using Google Maps Roads API.
To tackle this, here's what I'm currently doing:
From the polyline list, I'm selecting two end-points of the path A and B.
Finding out the bearing from A to B.
Creating a list of LatLng points between A and B that are 1 meter apart from each other.
Iterating through the list to check the GPS coordinates for the nearest LatLng.
Setting my marker's location to the nearest LatLng.
The above works in some cases but mostly the marker displays parallel to the polyline not on it.
Is there any other way to make my marker snap to the polyline?
Here is my own implementation for snapping marker to polyline (based on GPS location):
First, use the polyline points to create a list of LatLng that are at a smaller distance (I'm using a one meter distance specified in splitPathIntoPoints function):
for (int i = 0; i < polylinePoints.size(); i++) {
LatLng src = new LatLng(Double.parseDouble(polylinePoints.get(i).get("lat")), Double.parseDouble(polylinePoints.get(i).get("lng")));
if (polylinePoints.size() > i + 1) {
LatLng dest = new LatLng(Double.parseDouble(polylinePoints.get(i + 1).get("lat")), Double.parseDouble(polylinePoints.get(i + 1).get("lng")));
List<LatLng> splitPoints = splitPathIntoPoints(src, dest);
mSplitPoints.addAll(splitPoints);
} else {
break;
}
}
Code for splitPathIntoPoints:
public static List<LatLng> splitPathIntoPoints(LatLng source, LatLng destination) {
Float distance = findDistance(source, destination);
List<LatLng> splitPoints = new ArrayList<>();
splitPoints.add(source);
splitPoints.add(destination);
while (distance > 1) {
int polypathSize = splitPoints.size();
List<LatLng> tempPoints = new ArrayList<>();
tempPoints.addAll(splitPoints);
int injectionIndex = 1;
for (int i = 0; i < (polypathSize - 1); i++) {
LatLng a1 = tempPoints.get(i);
LatLng a2 = tempPoints.get(i + 1);
splitPoints.add(injectionIndex, findMidPoint(a1, a2));
injectionIndex += 2;
}
distance = findDistance(splitPoints.get(0), splitPoints.get(1));
}
return splitPoints;
}
Code for findDistance:
public static Float findDistance(LatLng source, LatLng destination) {
Location srcLoc = new Location("srcLoc");
srcLoc.setLatitude(source.latitude);
srcLoc.setLongitude(source.longitude);
Location destLoc = new Location("destLoc");
destLoc.setLatitude(destination.latitude);
destLoc.setLongitude(destination.longitude);
return srcLoc.distanceTo(destLoc);
}
Code for findMidPoint:
public static LatLng findMidPoint(LatLng source, LatLng destination) {
double x1 = toRad(source.latitude);
double y1 = toRad(source.longitude);
double x2 = toRad(destination.latitude);
double y2 = toRad(destination.longitude);
double Bx = Math.cos(x2) * Math.cos(y2 - y1);
double By = Math.cos(x2) * Math.sin(y2 - y1);
double x3 = toDeg(Math.atan2(Math.sin(x1) + Math.sin(x2), Math.sqrt((Math.cos(x1) + Bx) * (Math.cos(x1) + Bx) + By * By)));
double y3 = y1 + Math.atan2(By, Math.cos(x1) + Bx);
y3 = toDeg((y3 + 540) % 360 - 180);
return new LatLng(x3, y3);
}
Once mSplitPoints is filled with smaller polyline points that are separated by 1 meter apart from each other, the function below finds the snapped location on the polyline based on my current GPS location. Note that mMinorIndexTravelled is a private field in my class with initial value set to zero.
public static LatLng snapToPolyline(LatLng currentLocation) {
LatLng snappedLatLng = null;
Location current = new Location("current");
current.setLatitude(currentLocation.latitude);
current.setLongitude(currentLocation.longitude);
Integer minConfirmCount = 0;
float currentMinDistance = 0, previousMinDistance = 0;
List<Float> distances = new ArrayList<>();
for (LatLng point: mSplitPoints.subList(mMinorIndexTravelled, mSplitPoints.size() - 1)) {
Location pointLoc = new Location("pointLoc");
pointLoc.setLatitude(point.latitude);
pointLoc.setLongitude(point.longitude);
distances.add(current.distanceTo(pointLoc));
previousMinDistance = currentMinDistance;
currentMinDistance = Collections.min(distances);
if (currentMinDistance == previousMinDistance) {
minConfirmCount++;
if (minConfirmCount > 10) {
mMinorIndexTravelled = distances.indexOf(currentMinDistance) + mMinorIndexTravelled;
snappedLatLng = mSplitPoints.get(mMinorIndexTravelled);
break;
}
}
}
return snappedLatLng;
}

How can I implement Lanczos resampling after every canvas transform without having to make a new canvas?

UPDATE: Once I got this demo working... holy smokes, it's SLOW, like 12-16 seconds for only a level 2 render (when image is around 1000x2000 pixels). This is not even worth bothering with.
I found this really awesome and hopeful looking code in the top answer here: Resizing an image in an HTML5 canvas
//returns a function that calculates lanczos weight
function lanczosCreate(lobes){
return function(x){
if (x > lobes)
return 0;
x *= Math.PI;
if (Math.abs(x) < 1e-16)
return 1
var xx = x / lobes;
return Math.sin(x) * Math.sin(xx) / x / xx;
}
}
//elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes){
this.canvas = elem;
elem.width = img.width;
elem.height = img.height;
elem.style.display = "none";
this.ctx = elem.getContext("2d");
this.ctx.drawImage(img, 0, 0);
this.img = img;
this.src = this.ctx.getImageData(0, 0, img.width, img.height);
this.dest = {
width: sx,
height: Math.round(img.height * sx / img.width),
};
this.dest.data = new Array(this.dest.width * this.dest.height * 3);
this.lanczos = lanczosCreate(lobes);
this.ratio = img.width / sx;
this.rcp_ratio = 2 / this.ratio;
this.range2 = Math.ceil(this.ratio * lobes / 2);
this.cacheLanc = {};
this.center = {};
this.icenter = {};
setTimeout(this.process1, 0, this, 0);
}
thumbnailer.prototype.process1 = function(self, u){
self.center.x = (u + 0.5) * self.ratio;
self.icenter.x = Math.floor(self.center.x);
for (var v = 0; v < self.dest.height; v++) {
self.center.y = (v + 0.5) * self.ratio;
self.icenter.y = Math.floor(self.center.y);
var a, r, g, b;
a = r = g = b = 0;
for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
if (i < 0 || i >= self.src.width)
continue;
var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
if (!self.cacheLanc[f_x])
self.cacheLanc[f_x] = {};
for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
if (j < 0 || j >= self.src.height)
continue;
var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
if (self.cacheLanc[f_x][f_y] == undefined)
self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
weight = self.cacheLanc[f_x][f_y];
if (weight > 0) {
var idx = (j * self.src.width + i) * 4;
a += weight;
r += weight * self.src.data[idx];
g += weight * self.src.data[idx + 1];
b += weight * self.src.data[idx + 2];
}
}
}
var idx = (v * self.dest.width + u) * 3;
self.dest.data[idx] = r / a;
self.dest.data[idx + 1] = g / a;
self.dest.data[idx + 2] = b / a;
}
if (++u < self.dest.width)
setTimeout(self.process1, 0, self, u);
else
setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self){
self.canvas.width = self.dest.width;
self.canvas.height = self.dest.height;
self.ctx.drawImage(self.img, 0, 0);
self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
var idx, idx2;
for (var i = 0; i < self.dest.width; i++) {
for (var j = 0; j < self.dest.height; j++) {
idx = (j * self.dest.width + i) * 3;
idx2 = (j * self.dest.width + i) * 4;
self.src.data[idx2] = self.dest.data[idx];
self.src.data[idx2 + 1] = self.dest.data[idx + 1];
self.src.data[idx2 + 2] = self.dest.data[idx + 2];
}
}
self.ctx.putImageData(self.src, 0, 0);
self.canvas.style.display = "block";
}
...
img.onload = function() {
var canvas = document.createElement("canvas");
new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
//but feel free to raise it up to 8. Your client will appreciate
//that the program makes full use of his machine.
document.body.appendChild(canvas);
}
However, this implementation loads an image and renders it, end of story.
I have been trying to re-implement this code so that it does the filtering every time an existing canvas is scaled (think, zooming in and out of an image or document) without having to load a new image or create a new canvas.
How can I adapt it to work this way? Or is that even possible?
What you want to do is something like a singleton to reuse your canvas object. This will let you save the cost of create a new canvas object each time and you will reuse the same object
function getCanvas(){
var canvas;
if (typeof canvas === "undefined"){ canvas = document.createElement("canvas");}
return canvas;
}
img.onload = function() {
var canvas = getCanvas("canvas");
.... THE REST OF YOUR CODE .......
}
.
However this is not what slows your code, image scaling Algorithms are really heavy algorithms with intensive cpu use "usually make use of gpu acceleration at a really low level", and use advanced techniques like multiple bufferr and so others. here is a interesting tutorial in java.net on how image scaling works, it is in java but you can interpolate to any language.
Javascript is not ready for this techniques, so I recommend you to use the transformations available in the canvas api, as in the tutorial you read the efficient way is using the canvas2Dcontext.
var ctx = canvas.getContext("2d");
ctx.scale(2,2);

trying to get the cente lat/lon of a polygon

I am trying to populate a var "polyCenter" with the latlon for the center of a polygon and its driving me insane..
var paths = MapToolbar.features["shapeTab"]["shape_1"].getPath();
var i;
for (i = 0; i < paths.length; i++) {
bounds.extend(paths[i]);
}
polyCenter = bounds.getCenter();
alert(polyCenter);
It keeps returning (0,-180) for some reason..
Mathematically an irregular polygon does not have a center. They do however have a centroid (center of gravity) that is usually the approximate center.
Here is the equation to calculate the centroid of a polygon:
The equation in JavaScript:
function GetCentroid(paths){
var f;
var x = 0;
var y = 0;
var nPts = paths.length;
var j = nPts-1;
var area = 0;
for (var i = 0; i < nPts; j=i++) {
var pt1 = paths[i];
var pt2 = paths[j];
f = pt1.lat() * pt2.lng() - pt2.lat() * pt1.lng();
x += (pt1.lat() + pt2.lat()) * f;
y += (pt1.lng() + pt2.lng()) * f;
area += pt1.lat() * pt2.lng();
area -= pt1.lng() * pt2.lat();
}
area /= 2;
f = area * 6;
return new google.maps.LatLng(x/f, y/f);
}
Here is an updated fiddle based on #Argiropoulos answer.
Take a look at an example although this is not the center of a polygon except if it's a rectangle

Google Map API: How to center dynamicly markers in AS3?

I have several markers on my map and want to center dynamily each time I click on a selected point which show a bunch of markers group.
Does anyone know how to do that in As3?
You could try to use the a formula to get the centroid of the polygon drawn by your markers, assuming it's a polygon. If not, and they're a bunch of scattered points, you need to get the ones on that form the outer bounding segments first.Also, the code assumes the polygon is closed(loops), so the last point is your first point again.
function centreOfMass(polyPoints:Array):Point{
var cx:Number = 0;
var cy:Number = 0;
var area:Number = area(polyPoints);
var result:Point = new Point();
var i:Number,j:Number,n:Number = polyPoints.length;
var factor:Number = 0;
for(i = 0; i < n ; i++){
j = (i+1) % n;
factor = polyPoints[i].x * polyPoints[j].y - polyPoints[j].x * polyPoints[i].y;
cx += polyPoints[i].x + polyPoints[j].x * factor;
cy += polyPoints[i].y + polyPoints[j].y * factor;
}
area *= 6.0;
factor = 1 / area;
cx *= factor;
cy *= factor;
result.offset(cx,cy);//sets x and y to cx and cy
return result;
}
function area(polyPoints:Array):Number{
var i:int,j:int,n:int = polyPoints.length;
var area:Number = 0;
for(i = 0; i < n; i++){
j = (i+1) % n;
area += polyPoints[i].x * polyPoints[j].y;
area -= polyPoints[j].x * polyPoints[i].y;
}
area *= 0.5;
return area;
}
You create an array of points and you use the lat/lon coords as x,y coords. If you're using flash player 10, feel free to change the array into a Vector. and don't forget to do the import.flash.geom.Point.
I didn't come up with the code, I just ported what was on the amazing Paul Bourke website. Tons of handy stuff there.