Jooq and postGIS - kotlin

I am trying to get Jooq working with GIS queries and found some samples on SO. I have a question regarding one of the samples. I found the following function which builds a Polygon (How to select points within polygon in PostGIS using jOOQ?). Something like:
public static Field<?> stPolygon(Field<?> geom, int value) {
return DSL.field("ST_Polygon({0}, {1})", Object.class, geom, DSL.val(value));
}
I am trying to figure out how I can use this to build a polygon field from GIS coordinates (specified as doubles as a list of [latitude, longitude])
After that, if I want to select records where some column (point) lies within this polygon, how can I create my comparison operator. Would the field.in function work fine for such needs?

You cannot use the IN predicate to check if a geometry is "in" another geometry. You ahve to use the spatial predicates for that, instead. For example:
select st_contains(
st_makepolygon(st_makeline(array[
st_point(0, 0),
st_point(0, 1),
st_point(1, 1),
st_point(1, 0),
st_point(0, 0)
])),
st_point(0.5, 0.5)
)
Inspiration taken from this question. If you have a table containing geometries for your points, you can also aggregate the points to an array like this:
select st_contains(
st_makepolygon(st_makeline(array_agg(points.point))),
st_point(0.5, 0.5)
)
from points
Built in support starting from jOOQ 3.16
Starting from jOOQ 3.16, there's built-in GIS support in the commercial editions of jOOQ. Some natively supported functions can be seen in the manual:
Spatial functions
Spatial predicates

Related

How to create where statement based on result of multiset

So, i would like to filter my query by exact match in result of multiset. Any ideas how to do it in JOOQ?
Example:
val result = dsl.select(
PLANT_PROTECTION_REGISTRATION.ID,
PLANT_PROTECTION_REGISTRATION.REGISTRATION_NUMBER,
PLANT_PROTECTION_REGISTRATION.PLANT_PROTECTION_ID,
multiset(
select(
PLANT_PROTECTION_APPLICATION.ORGANISM_ID,
PLANT_PROTECTION_APPLICATION.ORGANISM_TEXT
).from(PLANT_PROTECTION_APPLICATION)
.where(PLANT_PROTECTION_APPLICATION.REGISTRATION_ID.eq(PLANT_PROTECTION_REGISTRATION.ID))
).`as`("organisms")
).from(PLANT_PROTECTION_REGISTRATION)
// here i would like to filter my result only for records that their organisms contain specific
// organism id
.where("organisms.organism_id".contains(organismId))
I've explained the following answer more in depth in this blog post
About the MULTISET value constructor
The MULTISET value constructor operator is so powerful, we'd like to use it everywhere :) But the way it works is that it creates a correlated subquery, which produces a nested data structure, which is hard to further process in the same SQL statement. It's not impossible. You could create a derived table and then unnest the MULTISET again, but that would probably be quite unwieldy. I've shown an example using native PostgreSQL in that blog post
Alternative using MULTISET_AGG
If you're not nesting things much more deeply, how about using the lesser known and lesser hyped MULTISET_AGG alternative, instead? In your particular case, you could do:
// Using aliases to make things a bit more readable
val ppa = PLANT_PROTECTION_APPLICATION.as("ppa");
// Also, implicit join helps keep things more simple
val ppr = ppa.plantProtectionRegistration().as("ppr");
dsl.select(
ppr.ID,
ppr.REGISTRATION_NUMBER,
ppr.PLANT_PROTECTION_ID,
multisetAgg(ppa.ORGANISM_ID, ppa.ORGANISM_TEXT).`as`("organisms"))
.from(ppa)
.groupBy(
ppr.ID,
ppr.REGISTRATION_NUMBER,
ppr.PLANT_PROTECTION_ID)
// Retain only those groups which contain the desired ORGANISM_ID
.having(
boolOr(trueCondition())
.filterWhere(ppa.ORGANISM_ID.eq(organismId)))
.fetch()

Selecting Columns Based on Multiple Criteria in a Julia DataFrame

I need to select values from a single column in a Julia dataframe based on multiple criteria sourced from an array. Context: I'm attempting to format the data from a large Julia DataFrame to support a PCA (primary component analysis), so I first split the original data into an anlytical matrix and a label array. This is my code, so far (doesn't work):
### Initialize source dataframe for PCA
dfSource=DataFrame(
colDataX=[0,5,10,15,5,20,0,5,10,30],
colDataY=[1,2,3,4,5,6,7,8,9,0],
colRowLabels=[0.2,0.3,0.5,0.6,0.0,0.1,0.2,0.1,0.8,0.0])
### Extract 1/2 of rows into analytical matrix
matSource=convert(Matrix,DataFrame(dfSource[1:2:end,1:2]))'
### Extract last column as labels
arLabels=dfSource[1:2:end,3]
### Select filtered rows
datGet=matSource[:,arLabels>=0.2 & arLabels<0.7][1,:]
print(datGet)
output> MethodError: no method matching...
At the last line before the print(datGet) statement, I get a MethodError indicating a method mismatch related to use of the & logic. What have I done wrong?
A small example of alternative implementation (maybe you will find it useful to see what DataFrames.jl has in-built):
# avoid materialization if dfSource is large
dfSourceHalf = #view dfSource[1:2:end, :]
lazyFilter = Iterators.filter(row -> 0.2 <= row[3] < 0.7, eachrow(dfSourceHalf))
matFiltered = mapreduce(row -> collect(row[1:2]), hcat, lazyFilter)
matFiltered[1, :]
(this is not optimized for speed, but rather as a showcase what is possible, but still it is already several times faster than your code)
This code works:
dfSource=DataFrame(
colDataX=[0,5,10,15,5,20,0,5,10,30],
colDataY=[1,2,3,4,5,6,7,8,9,0],
colRowLabels=[0.2,0.3,0.5,0.6,0.0,0.1,0.2,0.1,0.8,0.0])
matSource=convert(Matrix,DataFrame(dfSource[1:2:end,1:2]))'
arLabels=dfSource[1:2:end,3]
datGet=matSource[:,(arLabels.>=0.2) .& (arLabels.<0.7)][1,:]
print(datGet)
output> [0,10,0]
Note the use of parenthetical enclosures (arLabels.>=0.2) and (arLabels<0.7), as well as the use of the .>= and .< syntax (which forces Julia to iterate through a container/collection). Finally, and most crucially (since it's the part most people miss), note the use of .& in place of just &. The dot operator makes all the difference!

How do I create a custom Diesel query using SQL functions with user-provided inputs?

I want to perform a query that uses custom SQL functions of PostGIS bundles. For instance, I can run the subsequent query with psql:
SELECT * FROM places
WHERE earth_box(ll_to_earth(40.6333125,-8.659492), 20)
#> ll_to_earth(places.lat, places.lng);
ll_to_earth and earth_box are PostGIS functions. How can I make this query with Diesel with those values of lat and lng as input?
I browsed the documentation but I can't wrap my head around it.
This was the solution I ended up with:
pub fn get_near(lat: f32, lng: f32, conn: &PgConnection) -> Vec<Places> {
diesel::sql_query("SELECT * FROM places WHERE earth_box(ll_to_earth($1,$2), 20) #> ll_to_earth(places.lat, places.lng)")
.bind::<diesel::sql_types::Float, _>(lat)
.bind::<diesel::sql_types::Float, _>(lng)
.load(conn)
.expect("An error has occured")
}
Searching the Diesel documentation for function leads directly to the sql_function macro:
Diesel only provides support for a very small number of SQL functions. This macro enables you to add additional functions from the SQL standard, as well as any custom functions your application might have.

Using PostGIS extensions on PostgreSQL, why doesn't `ST_ConvexHull` contain all points that create it?

In the below query, I'm creating a ST_ConvexHull type from a collection of ST_Point types while querying that these points are contained within it. Most points return true from ST_Contains but a handful return false meaning they are not inside the hull. Any idea what's going on here? The hull should contain all of the points that created it.
SELECT
pc_compact,
latitude,
longitude,
ST_Contains(
(
SELECT ST_ConvexHull(ST_Collect(data.points))
FROM (
SELECT ST_Point(latitude, longitude) as points
FROM postcodes
WHERE pc_compact LIKE 'HU12%'
) as data
),
ST_Point(latitude, longitude)
) as inside
FROM postcodes
WHERE pc_compact LIKE 'HU12%'
Use ST_Intersects instead of ST_Contains.
There may be points that reside just on the boundary of convex hull. In that cast St_contains returns false, as that point is not totally inside the polygon.
You should try ST_Contains. It will select all the point even if they are on the boundary.
Maybe try use ST_envelope instead of ST_convexhull? Look like it would work as you expect.
I mean ST_Envelope(ST_Collect(..)).

converting Json coordinate into CLLocationCoordinate2D

I have a service returning geographies (points and polygons) the object return as strings for example:
"POINT (51.38494009999999 -0.3514684)"
I can do string manipulations to extract the two values but was wondering if there is a better way to convert that into CLLocationCoordinate2D.
mainly because of the polygons, inserting point by point doesn't seem like the correct solution.
If you want a more appropriate structure to represent a polygon, you could use the MKPolygon class from MapKit. It accepts an array of CLLocationCoordinate2D points as it's initialiser. Using this class may be handy if you are planning to interact with MapKit in your application. Otherwise, it is probably unnecessary to do so - a custom class will likely be the desired container then.
You are seeing well-known text, which was produced by ST_AsText. If you actually were expecting JSON and you are able to modify the server-side query, use ST_AsGeoJSON to instead return:
{"type":"Point","coordinates":[51.3849401,-0.3514684]}
If you have only point data, also consider returning the double precision data, ST_X for longitude and ST_Y for latitude. The last suggestion of course does not require any parsing of text results, but does not work on linestrings or polygons.