GEOSPARQL get the POINTs inside a wkt POLYGON - gis

I have an ontology installed on Virtuoso server.
In this ontology I have 2 class:
point of interests called POI
areas called AREA
Each POI has the relation:
{ POI gis:hasGeometry ?xx .
?xx gis:asWKT ?WKTexpression }
For example, WKTExpression can be "POINT(11.2589271 43.8004469)"^^gis:wktLiteral
Each AREA has the relation:
{ AREA gis:hasGeometry ?xx .
?xx gis:asWKT ?WKTexpression }
-- where WKTexpression is a "POLYGON()"^^gis:wktLiteral
How can I extract, with a GEOSPARQL query, all the POI inside a specific AREA?
Can I do something like this?
SELECT *
WHERE
{
?element gis:hasGeometry ?point .
?point <IS INSIDE> "POLYGON(...)"^^gis:wktLiteral
}
==================================
UPDATE 1:
I am trying this query in Virtuoso 7:
SELECT *
WHERE {
?sensor <http://www.w3.org/2003/01/geo/wgs84_pos#geometry> ?fWKT .
FILTER (ogcgsf:sfWithin(?fWKT, "POLYGON((11.170005798339842 43.845174931181276,11.157989501953123 43.83130720030931,11.152496337890625 43.82040886498103,11.160392761230469 43.78596696172772,11.172409057617186 43.75745685244749,11.179962158203125 43.74431283565997,11.215324401855467 43.72818887413058,11.244850158691406 43.72694838956602,11.27265930175781 43.74183250878846,11.299781799316404 43.76464763976463,11.331024169921873 43.79166735265696,11.29566192626953 43.824619821317356,11.216697692871094 43.841708300627744,11.172409057617186 43.84888895466105,11.170005798339842 43.845174931181276))"^^geo:wktLiteral))}
but I have returned:
Virtuoso 42001 Error SR185: Undefined procedure DB.DBA.http://www.opengis.net/def/function/geosparql/sfWithin.

Related

GEO location inversed PHP and SQL

I'm currently working on a campaign/advertisement script in Laravel 5.2. I'm having a table with ads, for example: Ad name, Location (lat/long), Radius (+10km).
Now I have a user location (lat/long). I want to see if he is in the radius of any ad and show the ad to him.
When I search on Google I only find solution to search ads based on lat/long + radius but I want to opposite. So see if a lat/long is in a radius of existing ads.
What is the best way to make this? And advice would be appreciated
This should work, all you would need to do is add this function to your User model.
public function ads()
{
return \App\Ad::
crossJoin('users', 'users.id', '=', \DB::raw($this->attributes['id']))
->where(\DB::raw('69 * DEGREES(ACOS(COS(RADIANS(users.Latitude)) * COS(RADIANS(ads.Latitude)) * COS(RADIANS(users.Longitude - ads.Longitude)) + SIN(RADIANS(users.Latitude)) * SIN(RADIANS(ads.Latitude))))'), '<=', 'ads.radius')
->get();
}
It's not a traditional Eloquent relationship so you won't be able to eager load it, although I'm sure that would be possible with more work.
In order to find a user's ads, you would simply do something like the following...
$ads = (new \App\User::find($user_id))->ads();
Note that this assumes both your users table and ads table contains a longitude and latitude column. It also assumes your ads table has a radius column. You might need to rename some columns but it should give you what you need.
Also this assumes your radius is in miles. If you use km, instead of 69, use 111.1111.
I have made some changes to the version of you user3158900.
Made a scope out of the function and the lat/long from the user is variable so I need them as a input in the query.
I now have this:
public function scopeAdsInLocation($query, $from_latitude, $from_longitude)
{
$query = CampaignModel::
where(\DB::raw('111.1111 * DEGREES(ACOS(COS(RADIANS(' . $from_latitude . ')) * COS(RADIANS(campaigns.loc_lat)) * COS(RADIANS(' . $from_longitude .' -
campaigns.loc_long)) + SIN(RADIANS(' . $from_latitude . ')) * SIN(RADIANS(campaigns.loc_lat))))'), '<=', 'campaigns.loc_radius')
->get();
return $query;
}
I call it like this:
$ads = CampaignModel::adsInLocation(51.191320, 5.987772);
var_dump($ads);
This code works, but only if I set the radius to a fixed value. So when I replace campaigns.loc_radius with '100' it works. But with the radius each campaign has it doesn't seem to do the job. Do you know maybe why? Or have a solution for this.

Mysql function MBRContains is not accurate

I have the following POLYGON (in the image you can see the area it covers)
POLYGON((-74.05100448502202 4.7239278424321,-74.05092938316898 4.7241416902206,-74.04830618275201 4.7237460717602,-74.04643668306903 4.7234306460692,-74.04635688735101 4.7234105978214,-74.04636526925401 4.7233310730989,-74.046191260944 4.72327293317,-74.04579027069599 4.7232007594583,-74.04141290558402 4.7214258184083,-74.03746201170497 4.7197791822891,-74.03565688503801 4.7189879401666,-74.033484295736 4.7180897723398,-74.03098447693401 4.7170526009038,-74.028731840457 4.7161167561787,-74.02852820211899 4.7150714370973,-74.026398371001 4.6877232674918,-74.02558060109601 4.6874859863574,-74.02454587610401 4.686797564651,-74.024665108676 4.6863189291555,-74.025470986757 4.6857975214267,-74.02585246812498 4.6846813784365,-74.02580479605103 4.6834369175226,-74.01962984798399 4.684922743491,-74.028472839649 4.6765444849623,-74.032273278366 4.6775012677607,-74.03825980124901 4.6799297676049,-74.048215993474 4.6850422042295,-74.05718496514402 4.6867981911917,-74.05100448502202 4.7239278424321))
When I execute MBRIntersect, MBRContains and Within functions they return that the green marker is inside of the polygon, but it is not (as you can see in the image). I'm executing the next sentence to get that:
SET #g1 = ST_GeomFromText('POLYGON((-74.05100448502202 4.7239278424321,-74.05092938316898 4.7241416902206,-74.04830618275201 4.7237460717602,-74.04643668306903 4.7234306460692,-74.04635688735101 4.7234105978214,-74.04636526925401 4.7233310730989,-74.046191260944 4.72327293317,-74.04579027069599 4.7232007594583,-74.04141290558402 4.7214258184083,-74.03746201170497 4.7197791822891,-74.03565688503801 4.7189879401666,-74.033484295736 4.7180897723398,-74.03098447693401 4.7170526009038,-74.028731840457 4.7161167561787,-74.02852820211899 4.7150714370973,-74.026398371001 4.6877232674918,-74.02558060109601 4.6874859863574,-74.02454587610401 4.686797564651,-74.024665108676 4.6863189291555,-74.025470986757 4.6857975214267,-74.02585246812498 4.6846813784365,-74.02580479605103 4.6834369175226,-74.01962984798399 4.684922743491,-74.028472839649 4.6765444849623,-74.032273278366 4.6775012677607,-74.03825980124901 4.6799297676049,-74.048215993474 4.6850422042295,-74.05718496514402 4.6867981911917,-74.05100448502202 4.7239278424321))', 4326);
SELECT MBRContains(#g1, ST_PointFromText('POINT(-74.051585 4.680108)', 4326)) g1,
st_distance(ST_PointFromText('POINT(-74.051585 4.680108)', 4326), #g1) distance
and I'm getting
g1 distance
1 | 0.005489581062607619
But I was expecting
g1 distance
0 | 0.005489581062607619
I have try the following cases:
Save geometry assigning 4326 SRID.
Using other functions, getting same response.
I'm Using 5.7.14 MySQL version
What am I doing wrong?
I worked around this issue just validating that there's no distance. But, why am I getting this result from that functions?
I don't have a MySQL to play with right now. So i first try to duplicate your query in a SQL Server Spatial.
DECLARE #g1 geometry
DECLARE #h1 geometry
SET #g1= geometry::STGeomFromText('POLYGON((-74.05100448502202 4.7239278424321,-74.05092938316898 4.7241416902206,-74.04830618275201 4.7237460717602,-74.04643668306903 4.7234306460692,-74.04635688735101 4.7234105978214,-74.04636526925401 4.7233310730989,-74.046191260944 4.72327293317,-74.04579027069599 4.7232007594583,-74.04141290558402 4.7214258184083,-74.03746201170497 4.7197791822891,-74.03565688503801 4.7189879401666,-74.033484295736 4.7180897723398,-74.03098447693401 4.7170526009038,-74.028731840457 4.7161167561787,-74.02852820211899 4.7150714370973,-74.026398371001 4.6877232674918,-74.02558060109601 4.6874859863574,-74.02454587610401 4.686797564651,-74.024665108676 4.6863189291555,-74.025470986757 4.6857975214267,-74.02585246812498 4.6846813784365,-74.02580479605103 4.6834369175226,-74.01962984798399 4.684922743491,-74.028472839649 4.6765444849623,-74.032273278366 4.6775012677607,-74.03825980124901 4.6799297676049,-74.048215993474 4.6850422042295,-74.05718496514402 4.6867981911917,-74.05100448502202 4.7239278424321))', 4326);
SET #h1 = geometry::STGeomFromText('POINT(-74.051585 4.680108)', 4326)
SELECT #g1.STContains(#h1) contain, #g1.STDistance(#h1) distance
And the result is what you may expect:
contain distance
0 | 0.005489581062607675
Here is the reason behind this:
I am using STContains not MBRContains based on your description of what you are looking for. MBRContains function first create a Minimal Bounding Rectangle over your polygon, and use that new polygon feature do the contain judegement. In your example, the point does fall into the MBR of your polygon so thats why your MySQL result is not what you expect. And STContains is the right function you are looking for.
Official reference: Mysql spatial Link

How optimize the research of next free "slot" in mysql?

i've a problem and i can't find an easy solution.
I have self expanding stucture made in this way.
database1 | table1
| table2
....
| table n
.
.
.
databaseN | table 1
table 2
table n
each table has a structire like this:
id|value
each time a number is generated is put into the right database/table/structure (is divided in this way for scalability... would be impossible to manage table of billions of records in a fas way).
the problem that N is not fixed.... but is like a base for calculating numbers (to be precise N is known....62 but I can onlyuse a subset of "digits" that could be different in time).
for exemple I can work only with 0 1 and 2 and after a while (when I've done all the possibilities) I want to add 4 and so on (up to base 62).
I would like to find a simple way to find the 1st free slot to put the next randomly generated id but that could be reverted.
Exemple:
I have 0 1 2 3 as numbers I want use....
the element 2313 is put on dabase 2 table 3 and there will be 13|value into table.
the element 1301 is put on dabase 1 table 3 and there will be 01|value into table.
I would like to generate another number based on the next free slot.
I could test every slot starting from 0 to the biggest number but when there will be milions of records for every database and table this will be impossible.
the next element of the 1st exemple would be 2323(and not 2314 since I'm using only the 0 1 2 3 digits).
I would like som sort of invers code in mysql to give me the 23 slot on table 3 database 2 to transform it into the number. I could randomly generate a number and try to find the nearest free up and down but since the set is variable could not be a good choice.
I hope it will be clear enought to tell me any suggestion ;-)
Use
show databases like 'database%' and a loop to find non-existent databases
show tables like 'table%' and a loop for tables
select count(*) from tableN to see if a table is "full" or not.
To find a free slot, walk the database with count in chunks.
This untested PHP/MySQL implementation will first fill up all existing databases and tables to base N+1 before creating new tables or databases.
The if(!$base) part should be altered if another behaviour is wanted.
The findFreeChunk can also be solved with iteration; but I leave that effort to You.
define (DB_PREFIX, 'database');
define (TABLE_PREFIX, 'table');
define (ID_LENGTH, 2)
function findFreeChunk($base, $db, $table, $prefix='')
{
$maxRecordCount=base**(ID_LENGTH-strlen($prefix));
for($i=-1; ++$i<$base;)
{
list($n) = mysql_fetch_row(mysql_query(
"select count(*) from `$db`.`$table` where `id` like '"
. ($tmp = $prefix. base_convert($i, 10, 62))
. "%'"));
if($n<$maxRecordCount)
{
// incomplete chunk found: recursion
for($k=-1;++$k<$base;)
if($ret = findFreeChunk($base, $db, $table, $tmp)
{ return $ret; }
}
}
}
function findFreeSlot($base=NULL)
{
// find current base if not given
if (!$base)
{
for($base=1; !$ret = findFreeSlot(++$base););
return $ret;
}
$maxRecordCount=$base**ID_LENGTH;
// walk existing DBs
$res = mysql_query("show databases like '". DB_PREFIX. "%'");
$dbs = array ();
while (list($db)=mysql_fetch_row($res))
{
// walk existing tables
$res2 = mysql_query("show tables in `$db` like '". TABLE_PREFIX. "%'");
$tables = array ();
while (list($table)=mysql_fetch_row($res2))
{
list($n) = mysql_fetch_row(mysql_query("select count(*) from `$db`.`$table`"));
if($n<$maxRecordCount) { return findFreeChunk($base, $db, $table); }
$tables[] = $table;
}
// no table with empty slot found: all available table names used?
if(count($tables)<$base)
{
for($i=-1;in_array($tmp=TABLE_PREFIX. base_convert(++$i,10,62),$tables););
if($i<$base) return [$db, $tmp, 0];
}
$dbs[] = $db;
}
// no database with empty slot found: all available database names used?
if(count($dbs)<$base)
{
for($i=-1;in_array($tmp=DB_PREFIX.base_convert(++$i,10,62),$dbs););
if($i<$base) return [$tmp, TABLE_PREFIX. 0, 0];
}
// none: return false
return false;
}
If you are not reusing your slots or not deleting anything, you can of course dump all this and simply remember the last ID to calculate the next one.

Mysql query within a given geographical area

A table in my database contains many postal addresses that also have longitude and latitude information like this:
Name ---- Street Address -------- Post Code ---- Longitude ---- Latitude
Fred ---- 11 Monarch Street ----- 4114 ---------- 57.317715 ---- 10.154355
Barney -- 4 Reign Street -------- 4114 ------------ 56.151112 ---- 10.087925
I have a given area (kml coordinates via google maps), defined like this:
12.548740,55.694469,0.000000
12.541320,55.687840,0.000000
12.537410,55.690552,0.000000
12.535310,55.694641,0.000000
12.534499,55.695293,0.000000
12.535787,55.696625,0.000000
12.538100,55.696911,0.000000
12.543890,55.697659,0.000000
12.548740,55.694469,0.000000
What mysql query do I use to find addresses located within in the area?
I don't know any MySQL query for this.
In this Answer I use a PHP function to find if a point is in a polygon (4points).The function is based on C code Point in Polygon by Darel Rex Finley
You can use it for your 8 point polygon.
$polySides = 8; //how many corners the polygon has
$polyX = array(12.548740,12.541320,12.537410,12.535310,12.534499,12.535787,12.538100,12.543890,12.548740);//horizontal coordinates of corners
$polyY = array(55.694469,55.687840,55.690552,55.694641,55.695293,55.696625,55.696911,55.697659,55.694469);//vertical coordinates of corners
$x = 12.548740;
$y = 55.694469;//Inside
//$y = 12.528740;//Outside
function pointInPolygon($polySides,$polyX,$polyY,$x,$y) {
$j = $polySides-1 ;
$oddNodes = 0;
for ($i=0; $i<$polySides; $i++) {
if ($polyY[$i]<$y && $polyY[$j]>=$y
|| $polyY[$j]<$y && $polyY[$i]>=$y) {
if ($polyX[$i]+($y-$polyY[$i])/($polyY[$j]-$polyY[$i])*($polyX[$j]-$polyX[$i])<$x) {
$oddNodes=!$oddNodes; }}
$j=$i; }
return $oddNodes; }
if (pointInPolygon($polySides,$polyX,$polyY,$x,$y)){
echo "Is in polygon!";
}
else echo "Is not in polygon";
You can loop through your database inserting lat,lng into function.
If the database is large you can create a bounding box with north_west and south_east
coordinates to limit the data to be filtered by the function.
$sql = "SELECT lat,lng FROM `table` WHERE (lng BETWEEN '$west_lng' AND '$east_lng') AND (lat BETWEEN '$north_lat' AND '$south_lat')";
The other solution is to use mysql spatial extension

MySQL - Perl: How to get array of zip codes within submitted "x" miles of submitted "zipcode" in Perl example

I have found many calculations here and some php examples and most are just over my head.
I found this example:
SELECT b.zip_code, b.state,
(3956 * (2 * ASIN(SQRT(
POWER(SIN(((a.lat-b.lat)*0.017453293)/2),2) +
COS(a.lat*0.017453293) *
COS(b.lat*0.017453293) *
POWER(SIN(((a.lng-b.lng)*0.017453293)/2),2))))) AS distance
FROM zips a, zips b
WHERE
a.zip_code = '90210' ## I would use the users submitted value
GROUP BY distance
having distance <= 5; ## I would use the users submitted value
But, I am having trouble understanding how to implement the query with my database.
It looks like that query has all I need.
However, I cannot even find/understand what b.zip_code actually is! (whats the b. and zips a, zips b?)
I also do not need the state in the query.
My mySQL db structure is like this:
ZIP | LAT | LONG
33416 | 26.6654 | -80.0929
I wrote this in attempt to return some kind of results (not based on above query) but, it only kicks out one zip code.
## Just for a test BUT, in reality I desire to SELECT a zip code WHERE ZIP = the users submitted zip code
## not by a submitted lat lon. I left off the $connect var, assume it's there.
my $set1 = (26.6654 - 0.20);
my $set2 = (26.6654 + 0.20);
my $set3 = (-80.0929 - 0.143);
my $set4 = (-80.0929 + 0.143);
my $test123 = $connect->prepare(qq{SELECT `ZIP` FROM `POSTAL`
WHERE `LAT` >= ? AND `LAT` <= ?
AND `LONG` >= ? AND `LONG` <= ?}) or die "$DBI::errstr";
$test123->execute("$set1","$set2","$set3","$set4") or die "$DBI::errstr";
my $cntr;
while(#zip = $test123->fetchrow_array()) {
print qq~$zip[$cntr]~;
push(#zips,$zip[$cntr]);
$cntr++;
}
As you can see, I am quite the novice so, I need some hand holding here with verbose explanation.
So, in Perl, how can I push zip codes into an array from a USER SUBMITTED ZIP CODE and user submitted DISTANCE in miles. Can be a square instead of a circle, not really that critical of a feature. Faster is better.
I'll tackle the small but crucial part of the question:
However, I cannot even find/understand what b.zip_code actually is! (whats the "b." and "zips a, zips b"?)
Basically, the query joins two tables. BUT, both tables being joined are in fact the same table - "zips" (in other words, it joins "zips" table to itself"). Of course, since the rest of the query needs to understand when you are referring to the first copy of the "zips" table and when to the second copy of the "zips" table, you are giving a table alias to each copy - to wit, "a" and "b"'.
So, "b.xxx" means "column xxx from table zips, from the SECOND instance of that table being joined".
I don't see what's wrong with your first query. You have latitude and longitude in your database (if I'm understanding, you're comparing a single entry to all others). You don't need to submit or return the state that's just part of the example. Make the first query work like this:
my $query = "SELECT b.zip_code,
(3956 * (2 * ASIN(SQRT(
POWER(SIN(((a.lat-b.lat)*0.017453293)/2),2) +
COS(a.lat*0.017453293) *
COS(b.lat*0.017453293) *
POWER(SIN(((a.lng-b.lng)*0.017453293)/2),2))))) AS distance
FROM zips a, zips b WHERE
a.zip_code = ?
GROUP BY distance having distance <= ?";
my $sth = $dbh->prepare($query);
$sth->execute( $user_submitted_zip, $user_submitted_distance );
while( my ($zip, $distance) = $sth->fetchrow() ) ) {
# do something
}
This won't be that fast, but if you have a small record set ( less than 30k rows ) it should be fine. If you really want to go faster you should look into a search engine such as Sphinx which will do this for you.
fetchrow_array returns a list of list references, essentially a two-dimensional array, where each row represents a different result from the database query and each column represents a field from the query (in your case, there is only one field, or column, per row).
Calling while ($test123->fetchrow_array()) will cause an infinite loop as your program executes the query over and over again. If the query returns results, then the while condition will be satisfied and the loop will repeat. The usual idiom would be to say something more like for my $row ($test123->fetchrow_array()) { ..., which will only execute the query once and then iterate over the results.
Each result is a list reference, and the zip code you are interested in is in the first (and only) column, so you could accumulate the results in an array like this:
my #zips = (); # for final results
for my $row ($test123->fetchrow_array()) {
push #zips, $row->[0];
}
or even more concisely with Perl's map statement:
my #zips = map { $_->[0] } $test123->fetchrow_array()
which does the same thing.