# frozen_string_literal: true # ********************************************************************************* # URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC. # See also https://github.com/urbanopt/urbanopt-geojson-gem/blob/develop/LICENSE.md # ********************************************************************************* # see the URL below for information on how to write OpenStudio measures # http://nrel.github.io/OpenStudio-user-documentation/measures/measure_writing_guide/ require 'json' require 'net/http' require 'uri' require 'openssl' require 'urbanopt/geojson' # start the measure class UrbanGeometryCreationZoning < OpenStudio::Measure::ModelMeasure attr_accessor :origin_lat_lon # human readable name def name return 'UrbanGeometryCreationZoning' end # human readable description def description return 'This measure reads an URBANopt GeoJSON and creates geometry with zoning for a particular building. Surrounding buildings are included as shading structures.' end # human readable description of modeling approach def modeler_description return '' end def arguments(model) args = OpenStudio::Measure::OSArgumentVector.new # geojson file geojson_file = OpenStudio::Measure::OSArgument.makeStringArgument('geojson_file', true) geojson_file.setDisplayName('GeoJSON File') geojson_file.setDescription('GeoJSON File.') args << geojson_file # feature id of the building to create feature_id = OpenStudio::Measure::OSArgument.makeStringArgument('feature_id', true) feature_id.setDisplayName('Feature ID') feature_id.setDescription('Feature ID.') args << feature_id # which surrounding buildings to include chs = OpenStudio::StringVector.new chs << 'None' chs << 'ShadingOnly' surrounding_buildings = OpenStudio::Measure::OSArgument.makeChoiceArgument('surrounding_buildings', chs, true) surrounding_buildings.setDisplayName('Surrounding Buildings') surrounding_buildings.setDescription('Select which surrounding buildings to include.') surrounding_buildings.setDefaultValue('ShadingOnly') args << surrounding_buildings return args end # define what happens when the measure is run def run(model, runner, user_arguments) super(model, runner, user_arguments) # use the built-in error checking if !runner.validateUserArguments(arguments(model), user_arguments) return false end # assign the user inputs to variables geojson_file = runner.getStringArgumentValue('geojson_file', user_arguments) feature_id = runner.getStringArgumentValue('feature_id', user_arguments) surrounding_buildings = runner.getStringArgumentValue('surrounding_buildings', user_arguments) default_construction_set = URBANopt::GeoJSON::Model.create_construction_set(model, runner) stories = [] model.getBuildingStorys.each { |story| stories << story } stories.sort! { |x, y| x.nominalZCoordinate.to_s.to_f <=> y.nominalZCoordinate.to_s.to_f } space_types = URBANopt::GeoJSON::Helper.create_space_types(stories, model, runner) # delete the previous building model.getBuilding.remove # create new building and transfer default construction set model.getBuilding.setDefaultConstructionSet(default_construction_set) # instance variables @runner = runner @origin_lat_lon = nil all_features = URBANopt::GeoJSON::GeoFile.from_file(geojson_file) feature = URBANopt::GeoJSON::GeoFile.from_file(geojson_file).get_feature_by_id(feature_id) # EXPOSE NAME name = feature.feature_json[:properties][:name] model.getBuilding.setName(name) # find min and max x coordinate @origin_lat_lon = feature.create_origin_lat_lon(@runner) site = model.getSite site.setLatitude(@origin_lat_lon.lat) site.setLongitude(@origin_lat_lon.lon) begin surface_elevation = feature.surface_elevation.to_f surface_elevation = OpenStudio.convert(surface_elevation, 'ft', 'm').get site.setElevation(surface_elevation) rescue StandardError @runner.registerWarning("Surface elevation not set for building '#{name}'") end case feature.type when 'Building' # make requested building, zoning is set to true spaces = feature.create_building(:spaces_per_floor, model, @origin_lat_lon, @runner, true) if spaces.nil? || spaces.empty? @runner.registerError("Failed to create building spaces for feature #{feature_id}") return false end # DLM: temp hack building_type = feature.building_type if building_type == 'Vacant' shading_surfaces = URBANopt::GeoJSON::Helper.create_shading_surfaces(feature, model, @origin_lat_lon, @runner, spaces) end # make other buildings to convert to shading convert_to_shades = [] if surrounding_buildings == 'ShadingOnly' convert_to_shades = feature.create_other_buildings(surrounding_buildings, all_features.json, model, @origin_lat_lon, @runner) end # intersect surfaces in this building with others @runner.registerInfo('Intersecting surfaces') spaces.each do |space| convert_to_shades.each do |other_space| space.intersectSurfaces(other_space) end end # match surfaces @runner.registerInfo('Matching surfaces') all_spaces = OpenStudio::Model::SpaceVector.new model.getSpaces.each do |space| all_spaces << space end OpenStudio::Model.matchSurfaces(all_spaces) # make windows spaces = feature.create_windows(spaces) # change adjacent surfaces to adiabatic model = URBANopt::GeoJSON::Model.change_adjacent_surfaces_to_adiabatic(model, @runner) # convert other buildings to shading surfaces convert_to_shades.map do |space| URBANopt::GeoJSON::Helper.convert_to_shading_surface_group(space) end when 'District System' district_system_type = feature[:properties][:district_system_type] if district_system_type == 'Community Photovoltaic' shading_surfaces = URBANopt::GeoJSON::Helper.create_photovoltaics(feature, 0, model, @origin_lat_lon, @runner) end else @runner.registerError("Unknown feature type '#{feature.type}'") return false end # transfer data from previous model stories = URBANopt::GeoJSON::Model.transfer_prev_model_data(model, space_types) return true end end # register the measure to be used by the application UrbanGeometryCreationZoning.new.registerWithApplication