Skip to content

Instantly share code, notes, and snippets.

@prestonmcgowan
Created June 4, 2019 13:43
Show Gist options
  • Save prestonmcgowan/3dd7d7d59216f705e6d7f2b16522df5a to your computer and use it in GitHub Desktop.
Save prestonmcgowan/3dd7d7d59216f705e6d7f2b16522df5a to your computer and use it in GitHub Desktop.
xquery version "1.0-ml";
(: Configurable variables :)
declare variable $initial-radius := 1; (: miles :)
declare variable $radius-step := 5; (: miles :)
declare variable $radius-max := 100; (: miles :)
declare variable $total-matches := 1;
declare variable $lat-long-element-container := "FacilitySite";
declare variable $lat-element := "LatitudeMeasure";
declare variable $lon-element := "LongitudeMeasure";
(: Variables used for state :)
declare variable $radius-not-found := -9999;
(: --- The code --- :)
(: WARNING: If a search does not match the total-matches requirement and the subsequent match matches a LARGE number of
: documents, the expanded tree cache could be filled and cause the order by in the bottom of the code to error out. It might make
: sense to write in an overload condition to catch this issue. You might be able to mitigate the issue by changing your radius-step to
: a smaller value.
:)
(: Geospatial search using a circle with a radius :)
declare function local:geo-search($radius, $point) {
cts:search(fn:doc(),
cts:element-pair-geospatial-query(xs:QName($lat-long-element-container),
xs:QName($lat-element), xs:QName($lon-element), cts:circle($radius, $point))
)
};
(: Return the number of matches for a given point and radius :)
declare function local:result-count($radius as xs:float, $point as cts:point)
as xs:integer {
xdmp:estimate(
cts:search(fn:doc(),
cts:element-pair-geospatial-query(xs:QName($lat-long-element-container),
xs:QName($lat-element), xs:QName($lon-element), cts:circle($radius, $point))
)
)
};
(: Recursive function to increase the radius until a satisfying radius has been found or radius-not-found :)
declare function local:expanding-radius-search($radius, $point) {
let $count := local:result-count($radius, $point)
return
if ($count >= $total-matches) then $radius
else
let $new-radius := $radius + $radius-step
return
if ($new-radius <= $radius-max) then
local:expanding-radius-search($new-radius, $point)
else
$radius-not-found
};
(: Change these values to change where you are starting your search :)
let $point-lat := 37.557151
let $point-lon := -122.4313
(: The main module code :)
(: Note: if the lat, lon elements have namespaces, the xdmp:value below needs to be changed to xdmp:with-namespaces :)
let $point := cts:point($point-lat, $point-lon)
let $radius := local:expanding-radius-search($initial-radius, $point)
return
if ($radius eq $radius-not-found) then
"No documents found"
else
let $matches :=
for $doc in local:geo-search($radius, $point)
order by cts:distance($point,
cts:point(xdmp:value(fn:concat("$doc//", $lat-element)), xdmp:value(fn:concat("$doc//", $lon-element)))
)
return $doc
return (text {"Radius used:", $radius}, $matches[1 to $total-matches] )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment