Class: URBANopt::GeoJSON::Feature

Inherits:
Core::Feature
  • Object
show all
Defined in:
lib/urbanopt/geojson/feature.rb

Direct Known Subclasses

Building, DistrictSystem, Region

Constant Summary collapse

@@feature_schema =
{}
@@schema_file_lock =
Mutex.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(feature) ⇒ Feature

Used to validate the feature using the validate_feat method.



18
19
20
# File 'lib/urbanopt/geojson/feature.rb', line 18

def initialize(feature)
  @feature_json = validate_feat(feature)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &blk) ⇒ Object



22
23
24
25
26
27
28
29
# File 'lib/urbanopt/geojson/feature.rb', line 22

def method_missing(name, *args, &blk)
  if @feature_json[:properties].keys.map(&:to_sym).include? name.to_sym

    return @feature_json[:properties][name.to_sym]
  else
    super
  end
end

Instance Attribute Details

#feature_jsonObject (readonly)

Returns the value of attribute feature_json.



11
12
13
# File 'lib/urbanopt/geojson/feature.rb', line 11

def feature_json
  @feature_json
end

Instance Method Details

#calculate_aspect_ratioObject

Used to calculate the aspect ratio for a given floor polygon.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/urbanopt/geojson/feature.rb', line 77

def calculate_aspect_ratio
  multi_polygons = get_multi_polygons(@feature_json)
  rad_per_deg = 0.017453293

  multi_polygons.each do |multi_polygon|
    if multi_polygon.size > 1
      runner.registerWarning('Ignoring holes in polygon')
    end
    multi_polygon.each do |polygon|
      n = polygon.size
      length = 0
      north = 0
      east = 0
      south = 0
      west = 0
      aspect_ratio = 0

      for i in (0..n - 2) do i
                             vertex_1 = nil
                             vertex_2 = nil
                             if i == n - 2
                               vertex_1 = polygon[n - 2]
                               vertex_2 = polygon[0]
                             else
                               vertex_1 = polygon[i]
                               vertex_2 = polygon[i + 1]
                             end
                             x_1 = vertex_1[0]
                             y_1 = vertex_1[1]
                             x_2 = vertex_2[0]
                             y_2 = vertex_2[1]

                             dist = (x_2 - x_1)**2 + (y_2 - y_1)**2

                             length = Math.sqrt(dist)

                             # delta latitude
                             dlat = x_2 - x_1
                             # delta longitude
                             dlon = y_2 - y_1

                             # convert radian to degree
                             sin_angle = Math.asin(dlon / length) * (1 / rad_per_deg)
                             sin_angle = sin_angle.round(4)

                             cos_angle = Math.acos(dlat / length) * (1 / rad_per_deg)
                             cos_angle = cos_angle.round(4)

                             if cos_angle >= 45 && cos_angle <= 135 && sin_angle >= 45 && sin_angle <= 90
                               north += length
                             elsif cos_angle >= 0 && cos_angle < 45 && sin_angle > -45 && sin_angle < 45
                               east += length
                             elsif  cos_angle >= 45 && cos_angle <= 135 && sin_angle >= -90 && sin_angle <= -45
                               south += length
                             elsif  cos_angle > 135 && cos_angle <= 180 && sin_angle > -45 && sin_angle < 45
                               west += length
                             end

                             if east + west != 0
                               aspect_ratio = (north + south) / (east + west)
                             else
                               aspect_ratio = 1
                             end

      end

      aspect_ratio = aspect_ratio.round(4)
      return aspect_ratio
    end
  end
end

#create_origin_lat_lon(runner) ⇒ Object

Returns instance of OpenStudio::PointLatLon for latitude and longitude of feature.

[Parameters]

  • runner - An instance of Openstudio::Measure::OSRunner for the measure run.


212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/urbanopt/geojson/feature.rb', line 212

def create_origin_lat_lon(runner)
  min_lon_lat = get_min_lon_lat
  min_lon = min_lon_lat[0]
  min_lat = min_lon_lat[1]

  if min_lon == Float::MAX || min_lat == Float::MAX
    runner.registerError('Could not determine min_lat and min_lon')
    return false
  else
    runner.registerInfo("Min_lat = #{min_lat}, min_lon = #{min_lon}")
  end

  return OpenStudio::PointLatLon.new(min_lat, min_lon, 0)
end

#feature_typeObject

Raises an error if the feature_type is not specified the the Feature's class.



48
49
50
# File 'lib/urbanopt/geojson/feature.rb', line 48

def feature_type
  raise 'feature_type not implemented for Feature, override in your class'
end

#find_feature_center(vertices) ⇒ Object

Used to determine the centroid for the feature's geojson coordinates.

[Parameters]

  • vertices - The first set polygon vertices in the array of feature coordinates. [Returns] Returns Feature centroid coordinates [long, lat]


234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/urbanopt/geojson/feature.rb', line 234

def find_feature_center(vertices)
  number_of_locations = vertices.length

  return vertices.first if number_of_locations == 1

  x = y = z = 0.0
  vertices.each do |station|
    latitude = station[1] * Math::PI / 180
    longitude = station[0] * Math::PI / 180

    x += Math.cos(latitude) * Math.cos(longitude)
    y += Math.cos(latitude) * Math.sin(longitude)
    z += Math.sin(latitude)
  end

  x /= number_of_locations
  y /= number_of_locations
  z /= number_of_locations

  central_longitude = Math.atan2(y, x)
  central_square_root = Math.sqrt(x * x + y * y)
  central_latitude = Math.atan2(z, central_square_root)

  [central_longitude * 180 / Math::PI,
   central_latitude * 180 / Math::PI]
end

#get_min_lon_latObject

Returns coordinate with the minimum longitute and latitude within a given building_json .



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/urbanopt/geojson/feature.rb', line 159

def get_min_lon_lat
  min_lon = Float::MAX
  min_lat = Float::MAX
  multi_polygons = get_multi_polygons
  multi_polygons.each do |multi_polygon|
    multi_polygon.each do |polygon|
      polygon.each do |point|
        min_lon = point[0] if point[0] < min_lon
        min_lat = point[1] if point[1] < min_lat
      end
      break
    end
  end
  return [min_lon, min_lat]
end

#get_multi_polygons(json = @feature_json) ⇒ Object

Returns MultiPolygon coordinates (coordinate pairs in double nested Array) [Parameters] json

For example:

polygon = { 'geometry': { 'type': 'Polygon', 'coordinates': [ [ [0, 5], [5, 5], [5, 0] ] ] } }



194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/urbanopt/geojson/feature.rb', line 194

def get_multi_polygons(json = @feature_json)
  geometry_type = json[:geometry][:type]
  multi_polygons = []
  case geometry_type
  when 'Polygon'
    polygons = json[:geometry][:coordinates]
    multi_polygons = [polygons]
  when 'MultiPolygon'
    multi_polygons = json[:geometry][:coordinates]
  end
  return multi_polygons
end

#get_perimeter_multiplier(area, aspect_ratio, perimeter_original) ⇒ Object

Used to calculate the perimeter multiplier given the aspect ratio, original perimeter and area.



151
152
153
154
155
# File 'lib/urbanopt/geojson/feature.rb', line 151

def get_perimeter_multiplier(area, aspect_ratio, perimeter_original)
  perimeter_new = 2 * (Math.sqrt(area * aspect_ratio) + Math.sqrt(area / aspect_ratio))
  perimeter_ratio = perimeter_original / perimeter_new
  return perimeter_ratio
end

#idObject

Returns the id of the feature.



34
35
36
# File 'lib/urbanopt/geojson/feature.rb', line 34

def id
  return @feature_json[:properties][:id]
end

#nameObject

Returns the name of the feature.



41
42
43
# File 'lib/urbanopt/geojson/feature.rb', line 41

def name
  return @feature_json[:properties][:name]
end

#schemaObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/urbanopt/geojson/feature.rb', line 59

def schema
  if @@feature_schema[feature_type].nil?
    @@schema_file_lock.synchronize do
      File.open(schema_file, 'r') do |file|
        @@feature_schema[feature_type] = JSON.parse(file.read, symbolize_names: true)

        # Allows additional properties.
        @@feature_schema[feature_type][:additionalProperties] = true
      end
    end
  end

  return @@feature_schema[feature_type]
end

#schema_fileObject

Raises an error if the schema_file is not specified the the Feature's class.



55
56
57
# File 'lib/urbanopt/geojson/feature.rb', line 55

def schema_file
  raise 'schema_file not implemented for Feature, override in your class'
end