Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support date shortcuts; see issue #13 #21

Merged
merged 24 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,104 @@
/Manifest.toml
# Created by https://www.toptal.com/developers/gitignore/api/julia,windows,linux,macos
# Edit at https://www.toptal.com/developers/gitignore?templates=julia,windows,linux,macos

### Julia ###
# Files generated by invoking Julia with --code-coverage
*.jl.cov
*.jl.*.cov

# Files generated by invoking Julia with --track-allocation
*.jl.mem

# System-specific files and directories generated by the BinaryProvider and BinDeps packages
# They contain absolute paths specific to the host computer, and so should not be committed
deps/deps.jl
deps/build.log
deps/downloads/
deps/usr/
deps/src/

# Build artifacts for creating documentation generated by the Documenter package
docs/build/
docs/site/

# File generated by Pkg, the package manager, based on a corresponding Project.toml
# It records a fixed state of all packages used by the project. As such, it should not be
# committed for packages, but should be committed for applications that require a static
# environment.
Manifest.toml

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

# End of https://www.toptal.com/developers/gitignore/api/julia,windows,linux,macos
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ version = "0.0.1"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53"
URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"

[compat]
HTTP = "1.10.8"
Preferences = "1.4.3"
TimeZones = "1.19.0"
URIs = "1.5.1"
julia = "1.10"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pkg> add Overpass

Get requested elements from OpenStreetMap through Overpass API. The result is provided as _string_ and has to be parsed depending on the format you specified in your query.

**💡 Tipp: Use [Overpass Turbo](https://overpass-turbo.eu/) to build your queries and use the *export* feature to save them as `.overpassql`**
**💡 Tipp: Use [Overpass Turbo](https://overpass-turbo.eu/) to build your queries and use the *export* feature to save them as `.overpassql` either as `standalone` or `raw`**

```julia
using Overpass
Expand Down
84 changes: 75 additions & 9 deletions src/Overpass.jl
gwehrle marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ using Preferences
using HTTP
using URIs
using Dates
using TimeZones

export query, set_endpoint, status, turbo_url

Expand Down Expand Up @@ -35,11 +36,16 @@ The query can be provided directly or as a path to a `.ql`/`.overpassql` file.
See also [`set_endpoint`](@ref) to change Overpass API endpoint.
"""
function query(
query_or_file::String; bbox::Bbox = nothing, center::Center = nothing)::String
query_or_file::String;
bbox::Bbox = nothing,
center::Center = nothing
)::String
url = @load_preference("endpoint", DEFAULT_ENDPOINT) * "interpreter"

query = get_query(query_or_file)
query = replace_shortcuts(query, bbox, center)
query = replace_date_shortcuts(query)
check_remaining_shortcuts(query)

try
# Start Overpass request
Expand Down Expand Up @@ -121,7 +127,7 @@ function status()::Status

status = Status(
matches[:connection_id],
DateTime(matches[:server_time], "yyyy-mm-ddTHH:MM:SSZ"),
DateTime(matches[:server_time], dateformat"yyyy-mm-ddTHH:MM:SSz"),
matches[:endpoint],
parse(Int, matches[:rate_limit]),
isnothing(matches[:avalible_slots]) ? nothing :
Expand Down Expand Up @@ -184,8 +190,6 @@ end
replace_shortcuts(query::AbstractString, bbox::Bbox, center::Center)::AbstractString

Fill shortcuts with defined replacements in the query.

Throws errors if unreplaced shortcuts are found.
"""
function replace_shortcuts(
query::AbstractString, bbox::Bbox, center::Center)::AbstractString
Expand All @@ -201,12 +205,76 @@ function replace_shortcuts(
@debug "query after center replacement" query
end

# check for remaining shortcuts
return query
end

"""
replace_date_shortcuts(query::AbstractString)::AbstractString

Fill date shortcuts with defined replacement pattern in the query.
See: https://wiki.openstreetmap.org/wiki/Overpass_turbo/Extended_Overpass_Turbo_Queries#Available_Shortcuts
"""
function replace_date_shortcuts(query::AbstractString)::AbstractString
if occursin("{{date", query)
# Get the current date and time
current_date = now()

# Define the regex pattern for placeholders
pattern = r"\{\{date(?::([0-9]+)\s*(year|month|day|week|hour|minute|second)s?)?\}\}"

# Map string units to Dates.Period constructors
period_map = Dict(
"year" => Year,
"month" => Month,
"day" => Day,
"week" => Week,
"hour" => Hour,
"minute" => Minute,
"second" => Second
)

for match in eachmatch(pattern, query)
# Extract captured groups
duration_value, duration_unit = match.captures
@debug "" match.captures

# Compute the replacement date
replacement_date = if duration_value !== nothing && duration_unit !== nothing
# Parse the duration and compute the past date
value = parse(Int, duration_value)
@debug "parsed duration value" value
period_constructor = get(period_map, duration_unit, nothing)
duration = period_constructor(value) # Create the period object
@debug "" duration
current_date - duration
else
# Use current date if no duration is provided
current_date
end
# make replacement_date a String in the correct format and appand 'Z' for UTC
replacement_date = Dates.format(replacement_date, ISODateTimeFormat) * "Z"
@debug "replacement String" replacement_date

# Replace the match in the query string
query = replace(query, match.match => replacement_date)
end
end

@debug "query afer date replacement" query
return query
end

"""
check_remaining_shortcuts(query::AbstractString)::nothing

Check a query for unreplaced overpass turbo shortcuts and throws errors if unreplaced shortcuts are found.
"""
function check_remaining_shortcuts(query::AbstractString)::nothing
for match in eachmatch(r"\{\{\s*(?<shortcut>\w+)\s*\}\}"i, query)
if match[:shortcut] == "bbox" && isnothing(bbox)
if match[:shortcut] == "bbox"
throw(MissingException("""{{bbox}} found in query, but no value specified.
Use keywordargument "bbox": Overpass.query(…, bbox = (48.22, 16.36, 48.22, 16.36))"""))
elseif match[:shortcut] == "center" && isnothing(center)
elseif match[:shortcut] == "center"
throw(MissingException("""{{center}} found in query, but no value specified.
Use keywordargument "center": Overpass.query(…, center = (48.22, 16.36))"""))
else
Expand All @@ -215,8 +283,6 @@ function replace_shortcuts(
Please consult the documentation for supported shortcuts"""))
end
end

return query
end

end
20 changes: 20 additions & 0 deletions test/queries/cycle_network.overpassql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @name Cycle Network

/*
This shows the cycleway and cycleroute network.
source: https://overpass-turbo.eu
*/

[out:json];

(
// get cycle route relations
relation[route=bicycle]({{bbox}});
// get cycleways
way[highway=cycleway]({{bbox}});
way[highway=path][bicycle=designated]({{bbox}});
);

out body;
>;
out skel qt;
12 changes: 12 additions & 0 deletions test/queries/drinking_water.overpassql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @name Drinking Water

/*
This is an example Overpass query.
source: https://overpass-turbo.eu
*/

[out:json];
node
[amenity=drinking_water]
({{bbox}});
out;
5 changes: 5 additions & 0 deletions test/queries/shortcut_date.overpassql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[timeout:180];
(
way[highway=construction]["opening_date"](if:date(t["opening_date"])>date("{{date:1 day}}"))(52.15624211595189,12.841649913704128,52.88073375719345,14.014440441047878);
);
out body geom qt;
8 changes: 8 additions & 0 deletions test/queries/shortcut_date_and_bbox.overpassql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @name date and bbox

[timeout:180];

(
way[highway=construction]["opening_date"](if:date(t["opening_date"])>date("{{date:1 day}}"))({{bbox}});
);
out body geom qt;
6 changes: 6 additions & 0 deletions test/queries/shortcut_different_dates.overpassql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @name Attic date query
(
nwr["highway"](48.2245225482161,16.359146833419803,48.22607178739413,16.36143743991852)(newer:"{{date:1year}}");
nwr[amenity](48.2245225482161,16.359146833419803,48.22607178739413,16.36143743991852)(newer:"{{date:12 years}}");
);
out body;
6 changes: 3 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ configure!(;

@test playback(
() -> Overpass.query(
string(@__DIR__, "/waterfountains.overpassql"), bbox = (
string(@__DIR__, "/queries/drinking_water_simple.overpassql"), bbox = (
48.224410300027, 16.36058699342046,
48.22702986850222, 16.364722959721423)),
"op-query") != ""
Expand Down Expand Up @@ -108,7 +108,7 @@ configure!(;
url = "https://overpass-turbo.eu/?Q=%5Bout%3Ajson%5D%3Bnode%5Bamenity%3Ddrinking_water%5D%28%7B%7Bbbox%7D%7D%29%3Bout%3B"
@test Overpass.turbo_url("[out:json];node[amenity=drinking_water]({{bbox}});out;") ==
url
@test Overpass.turbo_url(string(@__DIR__, "/waterfountains.overpassql")) == url
@test Overpass.turbo_url(string(@__DIR__, "/queries/drinking_water_simple.overpassql")) == url
end

@testset "unsescapehtml" begin
Expand All @@ -123,7 +123,7 @@ configure!(;
end

@testset "read file" begin
@test Overpass.get_query(string(@__DIR__, "/waterfountains.overpassql")) ==
@test Overpass.get_query(string(@__DIR__, "/queries/drinking_water_simple.overpassql")) ==
"[out:json];node[amenity=drinking_water]({{bbox}});out;"
end

Expand Down
Loading