Question-and-Answer Resource for the Building Energy Modeling Community
Get started with the Help page

# Openstudio SDK - Generate DOE building from OpenStreetMap

Hello,

First time here.

My goal is to generate a typical DOE building from OpenStreetMap data. In the Openstudio application I have used the following workflow:

• Use the background map in FloorspaceJS
• Trace the footprint
• Apply a typical WWR
• Assign stub spaces
• Apply the Create typical doe building from model measure

I would like to replicate this workflow with the Ruby SDK.

I understand how to do it if I have a initial model, but I am struggling to understand how to build a geometry in the SDK. I have had a look at the code of Create bar from space type ratios from openstudio-extension, but apart from being quite complex, it seems to generate only rectangular footprints.

Can you suggest an approach? I was thinking that maybe I could start by generating a gbXML model, but before embarking in a big task I wanted to look for advice.

EDIT: Initial progress

I managed to create an initial version. I have used two gems: openstreetmap and geoutm.

This is the code

require "/Applications/OpenStudio-3.4.0/Ruby/openstudio.rb"

# OSM
require "openstreetmap"
require "geoutm"

model_name = "testOSM"

osm_way_id = 183091005
api = OpenStreetMap::Api.new
no_stories = 2
floor_to_floor_height = 3.0

osm_nodes = api.find_way(osm_way_id).nodes

osm_footprint = []
osm_nodes[0..-2].each do |osm_node|
osm_node_obj = api.find_node(osm_node)
latlon = GeoUtm::LatLon.new osm_node_obj.lat, osm_node_obj.lon
osm_footprint << [latlon.to_utm.e, latlon.to_utm.n]
end

pp osm_footprint
x_centre = osm_footprint.map { |v| v[0] }.reduce(:+) / osm_footprint.size
y_centre = osm_footprint.map { |v| v[1] }.reduce(:+) / osm_footprint.size

osm_footprint.map! { |v| [v[0] - x_centre, v[1] - y_centre] }

model = OpenStudio::Model::Model.new
spaces = OpenStudio::Model::SpaceVector.new
(0..(no_stories - 1)).each do |level|

z0 = level * floor_to_floor_height

footprint_vertices = OpenStudio::Point3dVector.new
osm_footprint.reverse.each do |vertex|
footprint_vertices << OpenStudio::Point3d.new(vertex[0], vertex[1], z0)
end

space = OpenStudio::Model::Space.fromFloorPrint(footprint_vertices, floor_to_floor_height, model)
space = space.get
spaces << space
space.setName("Level #{level}")

story = OpenStudio::Model::BuildingStory.new(model)
story.setName("Floor #{level}")
space.setBuildingStory(story)

zone = OpenStudio::Model::ThermalZone.new(model)
space.setThermalZone(zone)
zone.setName("Zone #{space.name}")
end

spaces[0].intersectSurfaces(spaces[1])
spaces[0].matchSurfaces(spaces[1])

model.save("#{model_name}.osm", true)


I think I am using intersectSurfaces and matchSurfaces incorrecty as I still have a roof in the middle and I get these messages when I run it

[openstudio.model.Surface] <1> Initial area of surface 'Surface 16' 340.807 does not equal post intersection area 340.828
[openstudio.model.Surface] <1> Initial area of other surface 'Surface 17' 340.807 does not equal post intersection area 340.828


Same issue if I use the model level version.

OpenStudio::Model.intersectSurfaces(spaces)
OpenStudio::Model.matchSurfaces(spaces)


See attached picture.

edit retag close merge delete

Sort by » oldest newest most voted

@mortar.io If you go from an OpenStreetMap footprint to an OpenStudio space you could use the fromFloorprint method, used here in the create_bar code.. It takes in a point3dVector and an extrusion height. But this would give you a single space representing the full building footprint, which isn't ideal. In URBANopt we have code to create core and perimeter zones from a GeoJSON feature, you could leverage that code but change the input format. Note while OpenStudio has built in methods for GeoJSON it doesn't have any for OpenStreetMap, but I have seen some efforts to develop code to convert OpenStreetMap to GeoJSON.

The core and perimeter is ok for some building types such as office that can be modeled a singled blended space type across the entire footprint, but in some case you may want to manually sub-divide the footprint. One idea we had that has not been implemented was to convert from GeoJSON to FloorSpaceJS. So this automates the initial tracing, but then still requires manual sub-division and space type assignment.

Using gbXML is another option, but I think going into FloorSpaceJS to sub-divide is good unless you are also developing a method to subdivide. There are a number of tools with advanced generative design for building layouts; some of those tools have pathways to OpenStudio. Hope this helps. Please update this if you do decide to write code that others can use or contribute to.

more

Link about OpenStreetMap to GeoJSON, may be useful if you want to use the GeoJSON to core and perimeter OSM code. Yes, I realized the confusion with two different file formats each called OSM :(

( 2022-06-27 17:26:51 -0500 )edit

Thanks! I will give a go to the 'fromFloorprint' approach to begin with. The subdivision of the spaces will come later. I will report here once done.

( 2022-06-28 02:08:53 -0500 )edit

@David Goldwasser I have edited the question with my initial code

( 2022-06-29 08:48:08 -0500 )edit

@mortar.io that's great! Intersection is needed when spaces are offset such that one or the other, or both need to be split to result in duplicate but reversed surface in adjacent spaces. In this case I would skip intersection for now. Matching doesn't create geometry but just assigns boundary conditions. I think that should work in this case using OpenStudio::Model.matchSurfaces(spaces). I can look into later why intersection is creating issue here, vs. doing nothing, since intersection shouldn't be necessary.

( 2022-06-29 09:50:01 -0500 )edit

Thanks @David Goldwasser That makes sense. I think it was my intepretation of the word 'roof'. I assumed it meant external, but OS does not have a 'ceiling' surface. I have attached a picture with boundary rendering. I assume this means that this is a heat transfer surface.

( 2022-06-29 10:30:23 -0500 )edit