Class: OvertureMaps::Query

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/overture_maps/query.rb

Overview

Ad-hoc queries against Overture GeoParquet — no PostGIS import needed.

OvertureMaps.query(theme: "places", location: "Seattle").limit(10).each { |r| ... }
OvertureMaps.query(theme: "buildings", bbox: [47.5, -122.4, 47.7, -122.2]).count
OvertureMaps.query(theme: "places", bbox: "47.5,-122.4,47.7,-122.2").export("places.geojson")

Unlimited queries spool through the same cache files the import pipeline uses (so a query warms the cache for a later import and vice versa); limited queries use a throwaway temp file. Records are hashes with the geometry parsed to an RGeo feature.

Constant Summary collapse

EXPORT_EXTENSIONS =
{
  ".parquet" => "parquet",
  ".geojson" => "geojson",
  ".geojsonseq" => "geojsonseq",
  ".gpkg" => "gpkg"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(theme:, type: nil, bbox: nil, location: nil, release: nil, limit: nil) ⇒ Query

Returns a new instance of Query.

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
# File 'lib/overture_maps/query.rb', line 30

def initialize(theme:, type: nil, bbox: nil, location: nil, release: nil, limit: nil)
  @theme = theme
  @type = type || infer_type(theme)
  @release = Releases.validate!(release || Releases.current)
  @location = location
  @bbox = coerce_bbox(bbox) if bbox
  raise ArgumentError, "provide bbox: or location:" unless @bbox || @location

  @limit = limit && Integer(limit)
end

Instance Attribute Details

#releaseObject (readonly)

Returns the value of attribute release.



28
29
30
# File 'lib/overture_maps/query.rb', line 28

def release
  @release
end

#themeObject (readonly)

Returns the value of attribute theme.



28
29
30
# File 'lib/overture_maps/query.rb', line 28

def theme
  @theme
end

#typeObject (readonly)

Returns the value of attribute type.



28
29
30
# File 'lib/overture_maps/query.rb', line 28

def type
  @type
end

Instance Method Details

#bboxObject

The resolved bounding box (resolves a location name on first use).



47
48
49
# File 'lib/overture_maps/query.rb', line 47

def bbox
  @bbox ||= resolve_location
end

#countObject

Fast remote count via row-group pushdown — nothing is downloaded.



52
53
54
55
56
57
58
# File 'lib/overture_maps/query.rb', line 52

def count
  sql, params = Import::Downloader.bbox_query(
    theme: theme, type: type, release: release, bbox: bbox, limit: @limit
  )
  rows = QueryEngine.instance.query("SELECT count(*) AS n FROM (#{sql})", params)
  Integer(rows.first["n"])
end

#each(&block) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/overture_maps/query.rb', line 60

def each(&block)
  return enum_for(:each) unless block

  with_extract do |path|
    reader = Import::ParquetReader.new(theme: theme)
    reader.each_record(source: path) do |record|
      record["geometry"] = GeometryParser.parse(record["geometry"])
      block.call(record)
    end
  end
  self
end

#each_batch(size: 1000) {|batch| ... } ⇒ Object

Yields:

  • (batch)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/overture_maps/query.rb', line 73

def each_batch(size: 1000)
  return enum_for(:each_batch, size: size) unless block_given?

  batch = []
  each do |record|
    batch << record
    if batch.length >= size
      yield batch
      batch = []
    end
  end
  yield batch if batch.any?
  self
end

#export(path, format: nil) ⇒ Object

Writes the query result straight to a file; format inferred from the extension unless given explicitly.



90
91
92
93
94
95
96
# File 'lib/overture_maps/query.rb', line 90

def export(path, format: nil)
  format ||= EXPORT_EXTENSIONS[File.extname(path).downcase] or
    raise ArgumentError, "cannot infer format from #{path}; pass format:"

  downloader.extract_bbox(bbox, format: format, output_path: path, limit: @limit) or
    raise Error, "query returned no data"
end

#limit(count) ⇒ Object



41
42
43
44
# File 'lib/overture_maps/query.rb', line 41

def limit(count)
  self.class.new(theme: theme, type: type, bbox: bbox, location: @location,
                 release: release, limit: count)
end

#to_geojsonObject

A GeoJSON FeatureCollection hash. Materializes everything — use #export for large results.



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/overture_maps/query.rb', line 100

def to_geojson
  features = map do |record|
    geometry = record["geometry"]
    {
      type: "Feature",
      geometry: geometry && RGeo::GeoJSON.encode(geometry),
      properties: record.except("geometry", "bbox")
    }
  end
  { type: "FeatureCollection", features: features }
end