/* $RCSfile: HelloWall.java,v $ * $Revision: 1.3 $ $Date: 2012/02/23 19:36:17 $ * Auth: Dave Loffredo (loffredo@steptools.com) * * Copyright (c) 1991-2012 by STEP Tools Inc. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation is hereby granted, provided that this copyright * notice and license appear on all copies of the software. * * STEP TOOLS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. STEP TOOLS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. */ import java.io.IOException; import java.util.UUID; import java.nio.ByteBuffer; import com.steptools.schemas.ifc2x3.*; import com.steptools.schemas.ifc2x3.Schema; import com.steptools.stdev.*; import com.steptools.stdev.p21.*; import com.steptools.stdev.keystone.*; public class HelloWall { // HELLO WALL - This IFC "hello world" program creates a standard // wall and attaches property sets and quantities to it. Inspired // by the HelloWall IFC sample created by Peter Bonsma, but much // simplified and reorganized to just focus on property sets. // // This program writes a Part 21 file called "output_wall.ifc". // You can look at this text file with a text editor or STEP Part // 21 file browser to see how the information is represented. public static void main(String[] argv) throws STDevException, IOException { // Create a model to hold the instance data String output_name = "output_wall.ifc"; Model mod = new Model(Schema.SCHEMA); // Give the model some Part 21 header information Header header = Header.forModel(mod); header.getFileName().setOriginating_system ("IFC Hello Wall"); Population pop = (Population)mod.getPopulation(); // Make one shared representation context that gives the origin, // precision information, and the number of dimensions. See the // function below for the details. // IfcRepresentationContext ctx = makeGeometryContext(pop); // Make a project and basic spatial structure, returning a // building storey that we can attach walls to. IfcBuildingStorey storey = createProjectAndBuildingStorey(pop, ctx); //---------------------------------------- // COMMON WALL PROPERTY SET - Start by making the common wall // property set that will be shared among all of our walls. Refer // to the list of property sets in the IFC spec for details on // each of these properties. // IfcPropertySet ps_common = pop.newIfcPropertySet(); ps_common.setGlobalId (makeGuid()); ps_common.setOwnerHistory (makeOwnerHistory(pop)); ps_common.setName("Pset_WallCommon"); IfcPropertySingleValue p; SetIfcProperty props = new SetIfcProperty(); ps_common.setHasProperties(props); props.addValue (make_prop_id (pop, "Reference", "")); props.addValue (make_prop_label(pop, "AcousticRating", "")); props.addValue (make_prop_label(pop, "FireRating", "")); props.addValue (make_prop_bool (pop, "Combustible", false)); props.addValue (make_prop_label(pop, "SurfaceSpreadOfFlame", "")); p = make_prop(pop, "ThermalTransmittance"); p.setNominalValue(pop.newIfcValueIfcThermalTransmittanceMeasure(0.24)); props.addValue (p); props.addValue (make_prop_bool (pop, "IsExternal", true)); props.addValue (make_prop_bool (pop, "ExtendToStructure", true)); props.addValue (make_prop_bool (pop, "LoadBearing", true)); props.addValue (make_prop_bool (pop, "Compartmentation", false)); //---------------------------------------- // CREATE WALL PHYSICAL QUANTITIES - This is a group of parameters // for describing physical quantities like length, width, etc. It // can be shared, but will likely be unique to each wall. // IfcElementQuantity eq = pop.newIfcElementQuantity(); eq.setGlobalId (makeGuid()); eq.setOwnerHistory (makeOwnerHistory(pop)); eq.setName("BaseQuantities"); // Build the individual quantities. The quantity objects also // have a unit field, but we leave this null to use the default // unit from the project. In this case it is feet, square feet, // and cubic feet. // double thickness = 0.5; // used by material below SetIfcPhysicalQuantity qty = new SetIfcPhysicalQuantity(); eq.setQuantities(qty); qty.addValue (make_qty_length (pop, "NominalHeight", 8)); qty.addValue (make_qty_length (pop, "NominalLength", 10)); qty.addValue (make_qty_length (pop, "NominalWidth", thickness)); qty.addValue (make_qty_area (pop, "GrossFootprintArea", 5)); qty.addValue (make_qty_area (pop, "NetFootprintArea", 5)); qty.addValue (make_qty_area (pop, "GrossSideArea", 80)); qty.addValue (make_qty_area (pop, "NetSideArea", 80)); qty.addValue (make_qty_volume (pop, "GrossVolume", 40)); qty.addValue (make_qty_volume (pop, "NetVolume", 40)); //---------------------------------------- // CREATE WALL MATERIAL AND LAYERS - A standard wall is of uniform // thickness and this material and layer definition describes how // it is aligned with the reference line. // IfcMaterial mtl = pop.newIfcMaterial(); mtl.setName ("ice"); // BIM for an igloo :-) IfcMaterialLayer layer = pop.newIfcMaterialLayer(); layer.setMaterial(mtl); layer.setLayerThickness (thickness); IfcMaterialLayerSet lset = pop.newIfcMaterialLayerSet(); lset.setMaterialLayers(new ListIfcMaterialLayer()); lset.getMaterialLayers().addValue (layer); IfcMaterialLayerSetUsage lsu = pop.newIfcMaterialLayerSetUsage(); lsu.setForLayerSet(lset); lsu.setLayerSetDirection (IfcLayerSetDirectionEnum.AXIS2); lsu.setDirectionSense (IfcDirectionSenseEnum.POSITIVE); lsu.setOffsetFromReferenceLine (-thickness/2); // center on reference line //---------------------------------------- // CREATE A STANDARD WALL - Create instance, add common property // set, quantities, material, and assign to the building storey. // The wall that we will describe IfcWallStandardCase w1 = pop.newIfcWallStandardCase(); w1.setGlobalId (makeGuid()); w1.setOwnerHistory (makeOwnerHistory(pop)); w1.setName("Hadrian's Wall"); // Attach walls to the building storey IfcRelContainedInSpatialStructure rel_storey = pop.newIfcRelContainedInSpatialStructure(); rel_storey.setGlobalId (makeGuid()); rel_storey.setOwnerHistory (makeOwnerHistory(pop)); rel_storey.setRelatingStructure(storey); rel_storey.setRelatedElements(new SetIfcProduct()); rel_storey.getRelatedElements().addValue (w1); // add other walls to storey // Attach common property set to the wall IfcRelDefinesByProperties rel_pset = pop.newIfcRelDefinesByProperties(); rel_pset.setGlobalId (makeGuid()); rel_pset.setOwnerHistory (makeOwnerHistory(pop)); rel_pset.setRelatingPropertyDefinition (ps_common); rel_pset.setRelatedObjects(new SetIfcObject()); rel_pset.getRelatedObjects().addValue (w1); // add common property to other walls // Attach the quantity object to the wall IfcRelDefinesByProperties rel_qty = pop.newIfcRelDefinesByProperties(); rel_qty.setGlobalId (makeGuid()); rel_qty.setOwnerHistory (makeOwnerHistory(pop)); rel_qty.setRelatingPropertyDefinition (eq); rel_qty.setRelatedObjects(new SetIfcObject()); rel_qty.getRelatedObjects().addValue (w1); // probably make new quantities for other walls unless they happen // to be the same length and height // Attach the material definition to the wall IfcRelAssociatesMaterial rel_material = pop.newIfcRelAssociatesMaterial(); rel_material.setGlobalId (makeGuid()); rel_material.setOwnerHistory (makeOwnerHistory(pop)); // material is connected using a select rel_material.setRelatingMaterial(pop.newIfcMaterialSelect(lsu)); rel_material.setRelatedObjects(new SetIfcRoot()); rel_material.getRelatedObjects().addValue (w1); // add material to other walls Part21Writer writer = new Part21Writer(); writer.write(output_name, mod); } //---------------------------------------- // PROPERTY UTILITIES -- Create the various kinds of IfcProperty // subtypes and values that are used by property sets. // static IfcPropertySingleValue make_prop (Population pop, String name) { IfcPropertySingleValue prop = pop.newIfcPropertySingleValue(); prop.setName (name); // Leave value and unit null return prop; } static IfcPropertySingleValue make_prop_bool( Population pop, String name, boolean val ) { // Make a property with a simple boolean value IfcPropertySingleValue prop = make_prop (pop, name); prop.setNominalValue(pop.newIfcValueIfcBoolean(ExpBoolean.fromBoolean(val))); return prop; } static IfcPropertySingleValue make_prop_id( Population pop, String name, String val ) { // Make a property with a simple identifier value IfcPropertySingleValue prop = make_prop (pop, name); prop.setNominalValue(pop.newIfcValueIfcIdentifier(val)); return prop; } static IfcPropertySingleValue make_prop_label( Population pop, String name, String val ) { // Make a property with a simple label value IfcPropertySingleValue prop = make_prop (pop, name); prop.setNominalValue(pop.newIfcValueIfcLabel(val)); return prop; } static IfcQuantityLength make_qty_length( Population pop, String name, double val ) { IfcQuantityLength qty = pop.newIfcQuantityLength(); qty.setName (name); qty.setLengthValue(val); qty.setUnit(null); return qty; } static IfcQuantityArea make_qty_area( Population pop, String name, double val ) { IfcQuantityArea qty = pop.newIfcQuantityArea(); qty.setName (name); qty.setAreaValue(val); qty.setUnit(null); return qty; } static IfcQuantityVolume make_qty_volume( Population pop, String name, double val ) { IfcQuantityVolume qty = pop.newIfcQuantityVolume(); qty.setName (name); qty.setVolumeValue(val); qty.setUnit(null); return qty; } // ------------------------------------------------------------ // ------------------------------------------------------------ // SUPPORT FUNCTIONS -- Adjust these as needed // ------------------------------------------------------------ // ------------------------------------------------------------ static IfcRelAggregates createRelAggregates( Population pop, IfcObjectDefinition relating ) { // CREATE AGGREGATES RELATIONSHIP -- Assign the relating pointer // and then return the relationship so that we can add children to // the related attribute IfcRelAggregates rel = pop.newIfcRelAggregates(); rel.setGlobalId (makeGuid()); rel.setOwnerHistory (makeOwnerHistory(pop)); rel.setRelatingObject(relating); rel.setRelatedObjects(new SetIfcObjectDefinition()); return rel; } static IfcBuildingStorey createProjectAndBuildingStorey ( Population pop, IfcRepresentationContext ctx ) { // CREATE A PROJECT - Create the project object for the file, // assign the units for the file, and geometry context. IfcProject p = pop.newIfcProject(); p.setGlobalId (makeGuid()); p.setOwnerHistory (makeOwnerHistory(pop)); p.setName ("project"); p.setRepresentationContexts(new SetIfcRepresentationContext()); p.getRepresentationContexts().addValue(ctx); // Declare units so we know what the numbers in the geometry mean. // See the make_units.cxx file for details. // p.setUnitsInContext (makeUnits(pop)); // CREATE THE BUILDING IfcBuilding bldg = pop.newIfcBuilding(); bldg.setGlobalId (makeGuid()); bldg.setOwnerHistory (makeOwnerHistory(pop)); bldg.setName ("sample building"); bldg.setDescription (""); // Add the building as a child of the project in the spatial // structure. IfcRelAggregates rel = createRelAggregates (pop, p); rel.getRelatedObjects().addValue(bldg); // CREATE THE BUILDING STOREY IfcBuildingStorey stor = pop.newIfcBuildingStorey(); stor.setGlobalId (makeGuid()); stor.setOwnerHistory (makeOwnerHistory(pop)); stor.setName ("Level 1"); stor.setDescription (""); // Add the storey as a child of the building in the spatial // structure. rel = createRelAggregates (pop, bldg); rel.getRelatedObjects().addValue(stor); return stor; } static IfcGeometricRepresentationContext makeGeometryContext(Population pop) { // Create a geometric context IfcGeometricRepresentationContext ctx = pop.newIfcGeometricRepresentationContext(); // A file can have a "Model" context for the 3D view and a "Plan" // context for the 2D view // ctx.setContextIdentifier ("Model"); ctx.setContextType ("Design"); ctx.setCoordinateSpaceDimension (3); // geometric uncertainty for points ctx.setPrecision (1e-6); // Create a coordinate system origin for the context IfcCartesianPoint p1 = pop.newIfcCartesianPoint(); ListReal coords = new ListReal(); p1.setCoordinates(coords); coords.add(0.0); coords.add(0.0); coords.add(0.0); IfcAxis2Placement3D ap3d = pop.newIfcAxis2Placement3D(); ap3d.setLocation(p1); // The placement is referenced through a select type ctx.setWorldCoordinateSystem (pop.newIfcAxis2Placement(ap3d)); return ctx; } static IfcOwnerHistory makeOwnerHistory(Population pop) { String shortcut = "__CONSTANT OWNER HISTORY"; // Use shortcut to an owner history if we have already created one Object prop = pop.getModel().getProperty(shortcut); if (prop != null) { return (IfcOwnerHistory) prop; } IfcOwnerHistory hist = pop.newIfcOwnerHistory(); IfcOrganization org = pop.newIfcOrganization(); org.setName ("Foo Bar, Inc."); IfcPerson person = pop.newIfcPerson(); person.setGivenName ("John"); person.setFamilyName ("Doe"); IfcPersonAndOrganization po = pop.newIfcPersonAndOrganization(); po.setThePerson (person); po.setTheOrganization (org); IfcApplication app = pop.newIfcApplication(); app.setApplicationDeveloper (org); app.setVersion ("1.0"); app.setApplicationFullName ("sample program"); app.setApplicationIdentifier ("program id"); hist.setOwningUser (po); hist.setOwningApplication (app); hist.setState (null); hist.setChangeAction (IfcChangeActionEnum.ADDED); // Optional fields hist.setLastModifiedDate (Primitives.NULL_INTEGER); hist.setLastModifyingUser (null); hist.setLastModifyingApplication (null); // IFC timestamp is seconds since 1970-01-01 (unix epoch) hist.setCreationDate ((int) (System.currentTimeMillis()/1000)); pop.getModel().addProperty(shortcut, hist); // save shortcut for future calls return hist; } static IfcUnitAssignment makeUnits(Population pop) { // Specify foot as the length unit, degree as the angle unit, // square feet as area. Since these are described in terms of the // SI units meter and radian, just trim this code if using meters // //---------------------------------------- // LENGTH UNIT //---------------------------------------- // SI unit for meter, we do not add this to the context, but we // use it to define the foot unit. If you are using meters, just // add this to the context and omit the code below that build the // conversion-based unit instance. // IfcSIUnit m = pop.newIfcSIUnit(); m.setUnitType (IfcUnitEnum.LENGTHUNIT); m.setPrefix (null); m.setName (IfcSIUnitName.METRE); // Foot is described as a conversion-based unit in based on the // meter SI unit. Also show dimensional exponents for the basic // quantities (length, time, mass, etc) the unit describes. // IfcMeasureWithUnit mwu = pop.newIfcMeasureWithUnit(); IfcValue val = pop.newIfcValueIfcLengthMeasure (0.3048); // cvt factor ft->m mwu.setValueComponent(val); mwu.setUnitComponent(pop.newIfcUnit(m)); IfcConversionBasedUnit ft = pop.newIfcConversionBasedUnit(); ft.setName ("FOOT"); ft.setUnitType (IfcUnitEnum.LENGTHUNIT); ft.setDimensions (pop.newIfcDimensionalExponents()); ft.getDimensions().setLengthExponent(1); ft.setConversionFactor (mwu); // Add foot to the unit context // The individual units are wrapped in a select type IfcUnit u = pop.newIfcUnit(ft); IfcUnitAssignment units = pop.newIfcUnitAssignment(); SetIfcUnit uset = new SetIfcUnit(); units.setUnits(uset); uset.addValue (u); //---------------------------------------- // ANGLE UNIT //---------------------------------------- // The SI angle unit is radians. Do not add this to the context, // but use it to define the degree unit. // IfcSIUnit rad = pop.newIfcSIUnit(); rad.setUnitType (IfcUnitEnum.PLANEANGLEUNIT); rad.setPrefix (null); rad.setName (IfcSIUnitName.RADIAN); // One Degree is Degree == PI/180 radians. Create the measure // with unit to indicate this conversion factor. We also need // dimensional exponents but these are all zero for angles. // mwu = pop.newIfcMeasureWithUnit(); mwu.setValueComponent ( pop.newIfcValueIfcPlaneAngleMeasure(0.01745329252) // PI/180 ); mwu.setUnitComponent (pop.newIfcUnit(rad)); IfcConversionBasedUnit deg = pop.newIfcConversionBasedUnit(); deg.setName ("DEGREE"); deg.setUnitType (IfcUnitEnum.PLANEANGLEUNIT); deg.setDimensions (pop.newIfcDimensionalExponents ()); deg.setConversionFactor (mwu); // Add degree to the unit context u = pop.newIfcUnit(deg); units.getUnits().addValue (u); //---------------------------------------- // AREA UNIT //---------------------------------------- // SI unit for square meters used to define square feet. If you // are using square meters, just add this to the context and omit // the code below that build the conversion-based unit instance. // IfcSIUnit sqm = pop.newIfcSIUnit(); rad.setUnitType (IfcUnitEnum.AREAUNIT); rad.setPrefix (null); rad.setName (IfcSIUnitName.SQUARE_METRE); // Foot is described as a conversion-based unit in based on the // meter SI unit. Also show dimensional exponents for the basic // quantities (length, time, mass, etc) the unit describes. // mwu = pop.newIfcMeasureWithUnit(); mwu.setValueComponent ( pop.newIfcValueIfcAreaMeasure(0.09290304) // convert sqft->sqm ); mwu.setUnitComponent (pop.newIfcUnit(sqm)); IfcConversionBasedUnit sqft = pop.newIfcConversionBasedUnit(); sqft.setName ("SQUARE FOOT"); sqft.setUnitType (IfcUnitEnum.AREAUNIT); sqft.setDimensions (pop.newIfcDimensionalExponents()); sqft.getDimensions().setLengthExponent(2); sqft.setConversionFactor (mwu); // Add degree to the unit context u = pop.newIfcUnit(deg); units.getUnits().addValue (u); return units; } // Base 64 encode a 128bit identifier using the character set and // approach given in the IFC documentation. // public static String makeGuid() { final String base64char = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$"; UUID uuid = UUID.randomUUID(); StringBuilder s = new StringBuilder(22); ByteBuffer b = ByteBuffer.allocate(16); b.putLong(0, uuid.getLeastSignificantBits()); b.putLong(7, uuid.getMostSignificantBits()); // first byte do manually because the first char is a partial // value. As per the sample code in the ifc documentation. // Note that Java uses >>> for unsigned shift and >> for // signed shift // s.append (base64char.charAt((b.get(0) & 0xc0) >>> 6)); s.append (base64char.charAt((b.get(0) & 0x3f))); // Break the remaining 15 bytes into three bytes groups, then for // each, divide into four 6bit values. for (int i=1; i<15; i+=3) { s.append (base64char.charAt((b.get(i) & 0xfc) >>> 2)); s.append (base64char.charAt( ((b.get(i) & 0x03) << 4) | ((b.get(i+1) & 0xf0) >>> 4) )); s.append (base64char.charAt( ((b.get(i+1) & 0x0f) << 2) | ((b.get(i+2) & 0xc0) >>> 6) )); s.append (base64char.charAt((b.get(i+2) & 0x3f))); } return s.toString(); } }