Class: NSWTopo::GeoJSON::Collection

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/nswtopo/gis/geojson/collection.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(projection: DEFAULT_PROJECTION, features: [], name: nil) ⇒ Collection

Returns a new instance of Collection.



6
7
8
# File 'lib/nswtopo/gis/geojson/collection.rb', line 6

def initialize(projection: DEFAULT_PROJECTION, features: [], name: nil)
  @projection, @features, @name = projection, features, name
end

Instance Attribute Details

#featuresObject (readonly)

Returns the value of attribute features.



9
10
11
# File 'lib/nswtopo/gis/geojson/collection.rb', line 9

def features
  @features
end

#nameObject (readonly)

Returns the value of attribute name.



9
10
11
# File 'lib/nswtopo/gis/geojson/collection.rb', line 9

def name
  @name
end

#projectionObject (readonly)

Returns the value of attribute projection.



9
10
11
# File 'lib/nswtopo/gis/geojson/collection.rb', line 9

def projection
  @projection
end

Class Method Details

.load(json, projection: nil, name: nil) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/nswtopo/gis/geojson/collection.rb', line 11

def self.load(json, projection: nil, name: nil)
  collection = JSON.parse(json)
  crs_name = collection.dig "crs", "properties", "name"
  projection ||= crs_name ? Projection.new(crs_name) : DEFAULT_PROJECTION
  name ||= collection["name"]
  collection["features"].select do |feature|
    feature["geometry"]
  end.map do |feature|
    geometry, properties = feature.values_at "geometry", "properties"
    type, coordinates = geometry.values_at "type", "coordinates"
    raise Error, "unsupported geometry type: #{type}" unless TYPES === type
    GeoJSON.const_get(type)[coordinates, properties]
  end.then do |features|
    new projection: projection, features: features, name: name
  end
rescue JSON::ParserError
  raise Error, "invalid GeoJSON data"
end

Instance Method Details

#<<(feature) ⇒ Object Also known as: push



30
31
32
# File 'lib/nswtopo/gis/geojson/collection.rb', line 30

def <<(feature)
  tap { @features << feature }
end

#bboxObject



146
147
148
# File 'lib/nswtopo/gis/geojson/collection.rb', line 146

def bbox
  GeoJSON.polygon [bounds.inject(&:product).values_at(0,2,3,1,0)], projection: @projection
end

#bbox_centreObject



150
151
152
153
# File 'lib/nswtopo/gis/geojson/collection.rb', line 150

def bbox_centre
  midpoint = bounds.map { |min, max| (max + min) / 2 }
  GeoJSON.point midpoint, projection: @projection
end

#bbox_extentsObject



155
156
157
# File 'lib/nswtopo/gis/geojson/collection.rb', line 155

def bbox_extents
  bounds.map { |min, max| max - min }
end

#boundsObject

TODO: what about empty collections?



142
143
144
# File 'lib/nswtopo/gis/geojson/collection.rb', line 142

def bounds
  map(&:bounds).transpose.map(&:flatten).map(&:minmax)
end

#buffer(*margins, **options) ⇒ Object



133
134
135
136
137
138
139
# File 'lib/nswtopo/gis/geojson/collection.rb', line 133

def buffer(*margins, **options)
  map do |feature|
    feature.buffer(*margins, **options)
  end.then do |features|
    with_features features
  end
end

#clip(polygon) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/nswtopo/gis/geojson/collection.rb', line 125

def clip(polygon)
  OS.ogr2ogr "-f", "GeoJSON", "-lco", "RFC7946=NO", "-clipsrc", polygon.wkt, "/vsistdout/", "GeoJSON:/vsistdin/" do |stdin|
    stdin.puts to_json
  end.then do |json|
    Collection.load json, projection: @projection
  end
end

#dissolve_pointsObject



112
113
114
# File 'lib/nswtopo/gis/geojson/collection.rb', line 112

def dissolve_points
  with_features map(&:dissolve_points)
end

#each(&block) ⇒ Object



36
37
38
# File 'lib/nswtopo/gis/geojson/collection.rb', line 36

def each(&block)
  block_given? ? tap { @features.each(&block) } : @features.each
end

#explodeObject



94
95
96
# File 'lib/nswtopo/gis/geojson/collection.rb', line 94

def explode
  with_features flat_map(&:explode)
end

#map!(&block) ⇒ Object



40
41
42
# File 'lib/nswtopo/gis/geojson/collection.rb', line 40

def map!(&block)
  tap { @features.map!(&block) }
end

#merge(other) ⇒ Object

Raises:



102
103
104
105
# File 'lib/nswtopo/gis/geojson/collection.rb', line 102

def merge(other)
  raise Error, "can't merge different projections" unless @projection == other.projection
  with_features @features + other.features
end

#merge!(other) ⇒ Object

Raises:



107
108
109
110
# File 'lib/nswtopo/gis/geojson/collection.rb', line 107

def merge!(other)
  raise Error, "can't merge different projections" unless @projection == other.projection
  tap { @features.concat other.features }
end

#minimum_bbox_angle(*margins) ⇒ Object



159
160
161
# File 'lib/nswtopo/gis/geojson/collection.rb', line 159

def minimum_bbox_angle(*margins)
  dissolve_points.union.first.minimum_bbox_angle(*margins)
end

#multiObject



98
99
100
# File 'lib/nswtopo/gis/geojson/collection.rb', line 98

def multi
  with_features map(&:multi)
end

#reject!(&block) ⇒ Object



44
45
46
# File 'lib/nswtopo/gis/geojson/collection.rb', line 44

def reject!(&block)
  tap { @features.reject!(&block) }
end

#reproject_to(projection) ⇒ Object



48
49
50
51
52
53
54
# File 'lib/nswtopo/gis/geojson/collection.rb', line 48

def reproject_to(projection)
  return self if self.projection == projection
  json = OS.ogr2ogr "-t_srs", projection, "-f", "GeoJSON", "-lco", "RFC7946=NO", "/vsistdout/", "GeoJSON:/vsistdin/" do |stdin|
    stdin.puts to_json
  end
  Collection.load json, projection: projection
end

#reproject_to_wgs84Object



56
57
58
# File 'lib/nswtopo/gis/geojson/collection.rb', line 56

def reproject_to_wgs84
  reproject_to Projection.wgs84
end

#rotate_by_degrees!(angle) ⇒ Object



121
122
123
# File 'lib/nswtopo/gis/geojson/collection.rb', line 121

def rotate_by_degrees!(angle)
  map! { |feature| feature.rotate_by_degrees(angle) }
end

#to_hObject



60
61
62
63
64
65
66
67
# File 'lib/nswtopo/gis/geojson/collection.rb', line 60

def to_h
  {
    "type" => "FeatureCollection",
    "name" => @name,
    "crs" => { "type" => "name", "properties" => { "name" => @projection } },
    "features" => map(&:to_h)
  }.compact
end

#to_json(**extras) ⇒ Object



73
74
75
# File 'lib/nswtopo/gis/geojson/collection.rb', line 73

def to_json(**extras)
  to_h.merge(extras).to_json
end

#unionObject



116
117
118
119
# File 'lib/nswtopo/gis/geojson/collection.rb', line 116

def union
  return self if none?
  with_features [inject(&:+)]
end

#with_features(features) ⇒ Object



77
78
79
# File 'lib/nswtopo/gis/geojson/collection.rb', line 77

def with_features(features)
  Collection.new projection: @projection, name: @name, features: features
end

#with_name(name) ⇒ Object



81
82
83
# File 'lib/nswtopo/gis/geojson/collection.rb', line 81

def with_name(name)
  Collection.new projection: @projection, name: name, features: @features
end

#with_sql(sql, name: @name) ⇒ Object



85
86
87
88
89
90
91
92
# File 'lib/nswtopo/gis/geojson/collection.rb', line 85

def with_sql(sql, name: @name)
  json = OS.ogr2ogr *%w[-f GeoJSON -lco RFC7946=NO /vsistdout/ GeoJSON:/vsistdin/ -dialect SQLite -sql], sql do |stdin|
    stdin.puts to_json
  end
  Collection.load(json, projection: @projection).with_name(name)
rescue OS::Error
  raise "GDAL with SQLite support required"
end