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

rubocop:disable Style/MethodMissing



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

def method_missing(name, *args, &blk)
  # rubocop:enable Style/MethodMissing
  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.



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
148
149
# File 'lib/urbanopt/geojson/feature.rb', line 79

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.



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

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.



50
51
52
# File 'lib/urbanopt/geojson/feature.rb', line 50

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]



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
260
261
# File 'lib/urbanopt/geojson/feature.rb', line 235

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 .



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

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]
       ]
     ]
   }
 }


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

def get_multi_polygons(json = @feature_json)
  geometry_type = json[:geometry][:type]
  multi_polygons = []
  if geometry_type == 'Polygon'
    polygons = json[:geometry][:coordinates]
    multi_polygons = [polygons]
  elsif geometry_type == '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.



153
154
155
156
157
# File 'lib/urbanopt/geojson/feature.rb', line 153

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.



36
37
38
# File 'lib/urbanopt/geojson/feature.rb', line 36

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

#nameObject

Returns the name of the feature.



43
44
45
# File 'lib/urbanopt/geojson/feature.rb', line 43

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

#schemaObject



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

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.



57
58
59
# File 'lib/urbanopt/geojson/feature.rb', line 57

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