PHP / Mongo geoJSON Loop is not valid - php

I'm passing in some coordinates to mongo to do a geo search. It works fine if the coordinates don't intersect (for instance a figure eight). But when two lines intersect it gives the loop is not valid. Is there any way to find the intersection and split all these loops up?
Note there could be many.
EDIT: I added the sample query and error. Note that I understand why it's happening, I'm just wondering if there is some known way to split those loops up into separate polygon's (some algorithm or within Mongo).
Query:
db.items.find({
"address.location": {
"$geoWithin": {
"$geometry": {
"type": "Polygon",
"coordinates": [[
[-97.209091, 49.905691],
[-97.206345, 49.918072],
[-97.178879, 49.919399],
[-97.165146, 49.907903],
[-97.164459, 49.892865],
[-97.180939, 49.889326],
[-97.197418, 49.895077],
[-97.200165, 49.902596],
[-97.203598, 49.919399],
[-97.216644, 49.928682],
[-97.244797, 49.927356],
[-97.255096, 49.913209],
[-97.209091, 49.905691]
]]
}
}
}
});
Error:
Error: error: {
"waitedMS" : NumberLong(0),
"ok" : 0,
"errmsg" : "Loop is not valid: [
[ -97.209091, 49.905691 ]
[ -97.206345, 49.918072 ],
[ -97.17887899999999, 49.919399 ],
[ -97.16514599999999, 49.907903 ],
[ -97.16445899999999, 49.892865 ],
[ -97.180939, 49.889326 ],
[ -97.197418, 49.895077 ],
[ -97.200165, 49.902596 ],
[ -97.203598, 49.919399 ],
[ -97.216644, 49.928682 ],
[ -97.24479700000001, 49.927356 ],
[ -97.25509599999999, 49.913209 ],
[ -97.209091, 49.905691 ]
]
Edges 1 and 7 cross.
Edge locations in degrees: [-97.2063450, 49.9180720]-[-97.1788790, 49.9193990]
and [-97.2001650, 49.9025960]-[-97.2035980, 49.9193990]
",
"code" : 2
}
UPDATE
I've added an image of a brute force approach.
Polygon Slicing
Basically it's doing a look ahead on intersects.
If it finds one, it swaps out the points so that it stays within a loop.
It would add the cut off as a "starting point" in some queue.
When a look ahead goes around and finds it's own starting point we have a loop.
Then keep going through the "starting point" queue until it's empty.
The new set of polygons should contain all separate loops (in theory).
There are some issues with this though, it can get quite expensive going through all these loops. Say a max of 50 points would be about 1275 operations.
Also dealing with wrap around on 0/180 degreee coordinates could be a challenge.
Anyway, I didn't want to spend a whole day on this, I could even deal with a solution that does NOT deal with the wrap around condition.
Hoping there is a nice algorithm for this somewhere already I can just pop in (probably has some fancy technical term).
Also would be great if there was a more efficient approach then the brute force look-ahead.

It is because your coordinates are identical that creates an anomaly in the polygon shape: [-97.1788790, 49.9193990] and [-97.2035980, 49.9193990]. In your code remove or change any of the duplicate coordinate,
"coordinates": [[
[-97.209091, 49.905691],
[-97.206345, 49.918072],
[-97.178879, 49.919399], // this line
[-97.165146, 49.907903],
[-97.164459, 49.892865],
[-97.180939, 49.889326],
[-97.197418, 49.895077],
[-97.200165, 49.902596],
[-97.203598, 49.919399], // and this one
[-97.216644, 49.928682],
[-97.244797, 49.927356],
[-97.255096, 49.913209],
[-97.209091, 49.905691]
]]

As I mentioned in comments the better tool to query spatial data would be to use PostGIS.
For example PostGIS have the ST_validReason() to find the problem with polygon and a st_makevalid to fix but if this is not an option then I would create a service available to your PHP script with shapely python library https://github.com/Toblerity/Shapely
http://toblerity.org/shapely/manual.html
The first premise of Shapely is that Python programmers should be able
to perform PostGIS type geometry operations outside of an RDBMS
Certainly shapely is a popular tool, and you should get future help with in on StackOverflow or gis.stackexchange.com from more experienced users
I think the problem you are trying to solve is not as trivial as it sounds.
So I would perform following steps:
1 Find how to do it with shapely
similar questions:
Splitting self-intersecting polygon only returned one polygon in shapely in Python
2 create a simple php service which will pass query details to python script
3 Shapely will not prevent the creation of invalid polygon, but exceptions will be raised when they are operated on. So basically on such exception, I would call the script from step 1.
If this is just for one of query, I would just use QGIS software ( import points as CSV, create a new shapefile layer ( of type polygon ), and use Numerical vertex edit plugin )
update
I think the polygon should be fixed by the tool that created it so in this case, it should be a user. Maybe you fix that problem by looking from a different angle and if the shape is coming from the user and is drawn in GoogleMap in your app maybe enforce no intersection during drawing.
Found also this Drawing a Polygon ( sorting points and creating polygon with no intersection )

Related

Is it possible to read a certain number of characters after a certain number of digits from the start of the file

I'm setting up a web app, where users can choose the starting point and the number of characters to read from a text file containing 1 billion digits of pi.
I have looked, but I can't find any similar problems. Because I don't know what the starting digit is, I can't use other solutions.
Here is the function written in Python:
def pi(left : int, right : int):
f.seek(left+1)
return f.read(right)
For example, entering 700 as the starting point and 9 as the number of characters should return "Pi(700,9): 542019956".
Use fseek to move the file pointer to the position you need, and fread to read the amount of characters you need - just like your Python sample code.
Actually, this capability is built in to file_get_contents.
$substr = file_get_contents('pi_file.txt', false, null, 700, 9);
A handy feature of that function that I learned about just now after using it for the past 7 years.

Trim mongo object size

This is my document:
{
"_id": {
"$oid": "58e9a13999447d65550f4dd6"
},
"prices": {
"20170409": 701.09,
"20170408": 700.07
},
"stock": {
"20170409": 0,
"20170408": 0
}
}
I append a lot of fields in document objects (prices, stock) but over time it ends up being huge, and thats just one document. I have 200k documents and each have prices and stock objects.
Wondering if there's any way I could keep those object size to 30 fields max, that is, older entries purged on reaching the limit?
Take a look here! You can do it with a combination of $push and $slice, the example on the link sort the array too, don't know if that is your need, but I think you will have a good start from there.

geoPHP point in polygon

i need some help, google makes me sad
I got polygon http://geojson.io/#id=gist:anonymous/069b8a955897723ca256&map=4/58.75/48.03
And it covering a lot.
And i got geoPHP library https://github.com/phayes/geoPHP
Which got methods contains, covers, coveredBy...
And everything is normal, except that point like 74.224074, 96.428248 is always false for methods contains,coveredBy,covers, etc for this polygon. But it must be true.
Please, tell me why. I got a headache.
OR
Tell me please, how check that the lat lng is inside polygon. I tested about 3 libraries in perl and php, making my own code, etc... But the right results i got only from google containsLocation in javascript. But i have to use it on server side, not in browser. If there any chance to use containsLocation in nodejs it would be very nice.
Thank you
A little code for geoPHP:
$a='JSON FROM LINK';
$b=geoPHP::load("POINT(74.224074 96.428248)","wkt");
$a=geoPHP::load($a, 'json');
$result=$a->contains($b);
var_dump($result);
And it will be false
EDIT:
I think i got it.
GeoJson making coordinates in wrong way, longtitude, latitude. But it must be latitude, longtitude.
Will try and then write here if it works
ANSWER:
Use this www.birdtheme.org/useful/v3tool.html to make polygon on maps. It making it in right way(latitude,longtitude)
Working code for geoPHP:
include_once('geoPHP.inc');
$addr=file_get_contents('https://maps.googleapis.com/maps/api/geocode/json?address='.urlencode($_GET['address']).'&sensor=false');
foreach($addr->{results} as $addr1)
{
if(array_key_exists("geometry",$addr1)){
$lat=$addr1->{geometry}->{location}->{lat};
$lng=$addr1->{geometry}->{location}->{lng};
break;
}
}
$point1 = geoPHP::load("POINT($lat $lng)","wkt");
$ya200=geoPHP::load("POLYGON((41.51 12.3, 38.27 28.83, 32.55 41.84, 27.6 55.55, 29.54 71.37, 33.43 83.32, 36.6 94.92, 36.6 99.49, 35.17 111.45, 32.55 124.8, 35.17 130.78, 39.64 142.73, 43.58 152.58, 45.83 166.29, 56.17 163.83, 63.07 159.26, 68.66 154.69, 72.18 148.71, 75.93 140.63, 78.49 129.02, 80.3 114.26, 80.98 100.2, 81.2 87.54, 80.87 73.83, 79.62 59.41, 76.27 40.43, 71.07 28.13, 67.2 23.2, 63.55 20.04, 59.01 17.23, 54.16 15.12, 48.46 13.36,41.51 12.3))","wkt");
var_dump($ya200->contains($point1));
I am the maintainer of the geoPHP library (https://geophp.net). You have your latitude and longitude mixed up. WKT specifies that x comes first, then y, and then (optionally) z. This makes sense, as we usually do things in XYZ order.
It's conventional in mathematics for the horizontal axis to be X, and the vertical axis to be Y. If we think of a globe overlaid on a piece of graphing paper with X and Y axes specified, we can see that longitude is the horizontal axis, which corresponds to X, and the latitude is the vertical axis, which corresponds to Y. Therefore reading it as "longitude, latitude" makes perfect sense.
Regrettably, this sensical approach is backwards from the way mariners and other cartographers have described coordinates on the globe it throughout history. Historically it has often been described as "latitude, longitude".
This clash of history vs mathematical-convention is why you see different conventions in different systems. In Google Maps API its "latitude, longitude", while in OpenLayers it's "longitude, latitude".
In this particular case, you are using WKT, which is specified in "longitude, latitude" order.
WKT format expects POINT(lon, lat), with lon being first. You mixed up the order. Corrected:
geoPHP::load("POINT($lon $lat)","wkt");
I have answered almost the same question but don't know how to make a repost of it, so I just repeat it here (sorry)
If you use this PHP library (https://github.com/xopbatgh/sb-polygon-pointer), it may do all you need
(but first you need to convert GEOjson coordinates to lat/lng)
Plan to do:
Insert the coordinates of your polygon into array
Ask the library is the any point with lat/lng inside this polygon
$polygonBox = [
[55.761515, 37.600375],
[55.759428, 37.651156],
[55.737112, 37.649566],
[55.737649, 37.597301],
];
$sbPolygonEngine = new sbPolygonEngine($polygonBox);
$isCrosses = $sbPolygonEngine->isCrossesWith(55.746768, 37.625605);
// $isCrosses is boolean
After hours of struggle, I could not get the contains method to work with geoPHP. It turns out, as pointed out from this SO response that the geos library needs to be installed manually (no composer), which I could not get working after sifting through documentation on archive.org.
As an alternative, I found the mjaschen/phpgeo to be simple to use to do polygon contains point calculations. Simply load in your lat lons to objects and call the contains method as outlined in the documentation:
$polygon = new Polygon();
$polygon->addPoint(new Coordinate(12.3, 41.51));
$polygon->addPoint(new Coordinate(28.83, 38.27));
$polygon->addPoint(new Coordinate(41.84, 32.55));
$point = new Coordinate(74.224074, 96.428248)
var_dump($polygon->contains($point))
All advanced methods in geoPHP requires that GEOS is installed. If You are not able to install it but still want to use intersects test for point and polygon, then I have forked geoPHP and added Polygon->pointInPolygon and MultiPolygon->pointInPolygon methods for that purpose. Take a look:
https://github.com/sookoll/geoPHP
$point = \geoPHP::load('POINT (x y)','wkt');
$polygon = \geoPHP::load('POLYGON ((x y...))','wkt');
$point_is_in_polygon = $polygon->pointInPolygon($point);

getting the lemma of a word using wordnet

How can I get the lemma for a given word using Wordnet. I couldn't seem to find in the wordnet documentation what i want. http://wordnet.princeton.edu/wordnet/man/wn.1WN.html
For example for the word "books" i want to get "book" , ashes => ash , booking => book, apples => apple .... etc.
i want to achieve this using wordnet in command line and I cant find exact options to retrieve such case.
A php solution would also be of great help because I originally intend to use the wordnet php API but it seems the current one in their website isn't working.
Morphy is a morphological processor native to WordNet. The WordNet interfaces invoke Morphy to lemmatize a word as part of the lookup process (e.g. you query "enlightened", it returns the results for both "enlightened" and, via Morphy, "enlighten").
The interfaces don't include a feature that allows a user to directly access Morphy, so using it in command line is only possible if you write your own program using one of the WordNet APIs. You can find documentation for Morphy at the WordNet site.
As near as I can tell, the PHP interface is still available, although you may need to use WordNet 2.x.
If you can use another tool try TreeTagger.
I am not sure that WordNet implements it natively. NLTK has Morphy, which precisely does what you want, but it is implemented in Python though. You can write a small Python program to take input from the command line and return the lemma.
Search for 'Morphy' in the following link:
http://nltk.googlecode.com/svn/trunk/doc/api/nltk.corpus.reader.wordnet.WordNetCorpusReader-class.html
nltk.WordNetLemmatizer() also does the job. Search for 'Lemmatization' in the following link:
http://nltk.googlecode.com/svn/trunk/doc/book/ch03.html
NLTK website : http://www.nltk.org/
The WordNetLemmatizer in the nltk library will do what you need. here is python3 code:
#!Python3 -- this is lemmatize_s.py
import nltk
from nltk.stem import WordNetLemmatizer
from nltk.tokenize import word_tokenize
print ("This program will lemmatize your input until you ask for it to 'end'.")
while True:
sentence = input("Type one or more words (or 'end') and press enter:")
if (sentence == "end"):
break
tokens = word_tokenize(sentence)
lemmatizer = WordNetLemmatizer()
Output=[lemmatizer.lemmatize(word) for word in tokens]
print (Output);
Running this from the command line:
eyeMac2016:james$ python3 lemmatize_s.py
This program will lemmatize your input until you ask for it to 'end'.
Type one or more words (or 'end') and press enter:books ashes
['book', 'ash']
Type one or more words (or 'end') and press enter:end
eyeMac2016:james$

PHP numbers to words SPANISH!

Does anyone know of a free-licence PHP code that would convert numbers to words in spanish?
It's needed for my work for generating bills so it needs to be accurate.
My knowledge of spanish is practically non-existent, so it would probably be hard to write it myself; I don't know anything about spanish grammar.
Edit: I've written my own version of this, for now it works for only 3 digit numbers (on both sides of the decimal symbol), but it's a good start. If I ever get it big (5 languages planned atm), I'll probably share it on github. Don't bet on it though.
You may use PHP Intl extension to do that.
if (version_compare(PHP_VERSION, '5.3.0', '<')
|| !class_exists('NumberFormatter')) {
exit('You need PHP 5.3 or above, and php_intl extension');
}
$formatter = new \NumberFormatter('es', \NumberFormatter::SPELLOUT);
echo $formatter->format(1234567) . "\n";
Output:
un millón doscientos treinta y cuatro mil quinientos sesenta y siete
It works not only with Spanish but many other languages as well.
Is there an easy way to convert a number to a word in PHP?
From the above, you can derive the "word" from the number, and then translate it to any language you like using any of the Translate API's out there ... or your own lookups.
Edit
Another way you could employ is simply hard coding in a PHP file or a text file a big array of values:
$numTranslation = array(1 => "uno", 2 => "dos");
Then, in your code, just retrieve the echo $numTranslation[2] if the number was 2 to print out the spanish equivalent.
Edit 2
Just to make it a bit more complete, if you ever want to support multiple languages, and not just spanish:
$numTranslation = array(
1 => array("en" => "One", "es" => "uno"),
2 => array("en" => "Two", "es" => "dos")
);
And to print it out to the end user: echo $numTranslation[2]['es']; to get the Spanish equivalent...
The only thing I could find is a Perl script.
The code itself is easy to write in PHP, and from the Perl script you can get the logic and the Spanish words for numbers.
here's the words...
1 to 100
http://www.top-tour-of-spain.com/1-100-Numbers-In-Spanish.html
100+
http://www.intro2spanish.com/vocabulary/numbers/advanced.htm
or
http://www.donquijote.org/spanishlanguage/numbers/numbers2.asp
and I found some nicely formatted VBA here:
http://www.excelforum.com/excel-programming/637231-translating-numbers-into-words.html

Categories