Objects Service

The WindPro ObjectsService provides access to functionality related to objects in windPRO. The ObjectService allows for manipulating general properties of objects as well as object layers. Each object has an object type in windPRO scripting that allows to e.g. generate an object of this type or get a list of all objects of that type present in the project. Some objects have additional scripting services that allow access to more specialized functionality and properties of these objects. The table below gives a list of all object types in windPRO scripting and a link to the dedicated service if available.

Objects available in scripting

Object type

Description

Scripting Service

AreaObj

Area object

ObjAreaService

BusBar

not available

Camera

not available

CtrlPoint

Control point

ObjCtrlPointService

EGrid

EGrid object

not available

ELoad

not available

ExtGrid

not available

ExistWTG

Existing ETG

ObjWtgService

HCData

Line data

ObjLineService

HCGridObj

Height contour grid

ObjElevationGridService

MeteoObjectData

Meteo object

ObjMeteoService

NewWTG

New WTG

ObjWtgService

NSA

Noise Receptor or area

ObjNSAService

Obj3DData

not available

Obstacle

Obstacle object

ObjObstacleService

Polygons

not available

Profile

not available

RadarObject

Radar

ObjRadarService

RoadObject

Road object

not available

ResultLayerObject

Result Layers

not available

Ruler

Ruler for measuring

ObjRulerService

Shadow

Shadow receptor

ObjShadowService

SolarPvObject

Solar PV object

not available

Shape

Shape

not available

SiteCenter

Site center

not available

SiteData

Site data object

ObjSiteDataService

Transformer

not available

Unknown

Not known type

not available

UsrTextData

Text box

ObjTextService

VR

not available

VRCamera

not available

WTGareas

WTG area

ObjWtgAreasService

WTGnet

not available

In the following a few examples are given on how to use the the ObjectsService

To use, create, and edit objects and layers in windPRO with a python script you need to make an instance of the service for objects. It is done with windproapi.get_service(), which takes in the type of service as a parameter. The code below is an example of the setup needed to use the windproapi.

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi

_windproapi = WindProApi()
_windproapi.start_windpro_random_port()
objects_service = _windproapi.get_service('ObjectsService')

Manipulate Objects

With GetObjectTypes you get a list of all the object types in windPRO, and with GetObjectCount you get the number of objects in your project. The code below shows an example of the method.

project_service = _windproapi.get_service('ProjectService')
working_dir = os.path.join(get_windpro_sample_path("4.0"), 'New Salem\\4.0')
project_path = os.path.join(working_dir, 'New Salem.w40p')
project_service.LoadFromFile(project_path)

# objTypes stores a list of object types
objTypes = objects_service.GetObjectTypes()

# objCount stores the number of objects
objCount = objects_service.GetObjectCount()

GetObject returns an object as a dictionary with it’s properties, it takes the index of the object as a parameter. Like GetObject, GetObjectFromHandle also returns an object a dictionary but takes in the handle of the object as a parameter. Getting objects by handle is recommended, as the handle will not change if objects are added or deleted. You can assign properties directly using “.” and then you can apply the changes with SetObjectPropsFromHandle, where you overwrite the object. The code below shows an example on these methods as well as an example of a windPRO object in python.

# GetObject() stores information about and object as a dictionary
obj = objects_service.GetObject(1)

# result of obj
"""
{
'Handle': 390762937,
'TypeId': 'NewWTG',
'ObjType': 0,
'ApiObjType': 'NewWTG',
'Lng': -101.406510917218,
'Lat': 46.7555215047512,
'Z': 700.0,
'Count': 1,
'Angle': 0.0,
'UserDescription': 'T24',
'AutoLevel': True,
'OffsetZ': 0.0,
'UserLabel': 'T24',
'SymbolVisible': True
}
"""

# GetObjectFromHandle() stores information about and object as a dictionary
objFromHandle = objects_service.GetObjectFromHandle(Handle)

# Make some changes to the object before applying them with SetObjectPropsFromHandle()
obj.Lng = -101.40
obj.Lat = 46.75

# SetObjectPropsFromHandle() set the properties in the object given as a parameter and returns it with the new information.
obj = objects_service.SetObjectPropsFromHandle(obj)

# GetObjects() returns all objects of a specific type
objects_service.GetObjects('NewWTG')

Often it is needed to get objects of a specific type. You can also get a list of all objects of a specific type with GetObjects which takes the object type as a parameter. All objects types are available from GetObjectTypes.

# objTypes stores a list of object types
objTypes = objects_service.GetObjectTypes()
print(objTypes)

# Getting all turbines that are in the project. NewWTG in contrast to ExistWTG
new_wtgs_objs = objects_service.GetObjects("NewWTG")

Interaction with windPRO scripting are kept to a relatively basic level for getting object. Getting objects by index can be useful if it is necessary to loop through all objects in the project. Getting objects by handle ensures that this is the exact object that was intended. Often it is necessary to find a specific object from this list of objects, e.g, from its Description in windPRO. With scripting functionality like this can be easily made within the scripting language.

# Getting all turbines that are in the project. NewWTG in contrast to ExistWTG
new_wtgs_objs = objects_service.GetObjects("NewWTG")

# Defining function that loops through a
def get_obj_from_list_by_user_description(objs, user_description):
    l_obj = []
    for o in objs:
        if o["UserDescription"] == user_description:
            l_obj.append(o)
    if len(l_obj) == 1:
        return l_obj[0]
    elif len(l_obj) == 0:
        raise ValueError(f"Could not find {user_description} in any UserDescription.")
    else:
        raise ValueError(f"More than one variable UserDescription {user_description}.")

obj_T24 = get_obj_from_list_by_user_description(new_wtgs_objs, "T24")

You can add, save, load, delete, and clone an object using the methods shown below. With AddObject you need to give a type, latitude, longitude, and a name of the object. SaveObject needs the handle of the object you want to save and the path where you want to save it. LoadObject need the path of the file you want to load. DeleteObject and CloneObject need the handle of the object you want to alter. The code below is an example of these methods.

# AddObject(type, lat, lon, name) adds a new object of a specific type with a name and placed on parsed coordinates.
objects_service.AddObject('NewWTG', 46.75, -101.40, 'New WTG obj')

# CloneObject(handle) copies an object and returns the properties as a dictionary
cloneObj = objects_service.CloneObject(Handle)

# SaveObject(int handle, string path) saves an object to a file and returns true on succes
saveObj = objects_service.SaveObject(Handle, filePath)

# DeleteObject(int handle) deletes an object
objects_service.DeleteObject(Handle)

# LoadObject() loads an object from a file and returns true on succes
loadObj = objects_service.LoadObject(filePath)

Manipulate layers

You can get basic information about layers from the ObjectService. It is possible to get the number of layers and the handle of that layer by index. The name of the layer is connected via the layer handle.

# return the count of all layers
layerCount = objects_service.GetLayerCount()

# returns the handle of the layer with the count given
layerHandle = objects_service.GetLayerHandle(1)

# returns the name of the layer by handle
layerName = objects_service.GetLayerName(layerHandle)

You can add, rename, and delete layers simple by following the example shown below. AddLayer returns a handle that can be used to modify this layer. Adding layers requires the handle of the parent folder. If the layer is not supposed to be in a sub folder you need to indicate this by using a 0.

# Adds a layer with a given name and returns the handle
layer = objects_service.AddLayer('newlayer', 0)

# Renames a layer with the handle stored in the layer variable
objects_service.RenameLayer(layer, 'RenamedLayer')

# Deletes layer from handle
objects_service.DeleteLayer(layer)

You can get the current layer in editmode and change it to another layer with the example shown below.

# returns the current edit layer
editLayer = objects_service.GetEditLayer()

# set a layer to edit layer with a handle
objects_service.SetEditLayer(layerHandle)

Layers are like a folder structure but the output from windPRO is a flat list where layers are linked to their parent folder by handle. Transforming the existing layer structure into a dictionary in python is possible with the function get_layer_dict provided in the python package. Be aware that layer folder names need to be unique for this function to work properly.

A full running example showing the presented features is given below. You need the sample project New Salem to execute the script.

Result Layers

Result layers are also handles as objects in windPRO. They have a location, even though they are not shown on the windPRO GUI. A good way is to choose the location of the site center for all result layers. Manually adding result layers is necessary for some calculations to display the result in windPRO.

"""
Copyright 2023 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from pprint import pprint
from windproapi.utils import get_windpro_sample_path
from windproapi.windproapi import get_layer_dict, nan_to_skipvalue
from windproapi import WindProApi

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.0'), 'New Salem\\4.0')
project_path = os.path.join(working_dir, 'New Salem.w40p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')

# Open project
project_service.LoadFromFile(filename=project_path)

# Getting all objects that could be included in windPRO
print(objects_service.GetObjectTypes())

for s in objects_service.GetObjectTypes():
    print(f'{s}')

# Getting all turbines that are in the project. NewWTG in contrast to ExistWTG
new_wtgs_objs = objects_service.GetObjects(apiObjType='NewWTG')

# Getting information by handle
handle = new_wtgs_objs[0].Handle
obj = objects_service.GetObjectFromHandle(handle=handle)


# Defining function that loops through the output from objects_service.GetObjects to get an object back by its
# user description
def get_obj_from_list_by_user_description(objs, user_description):
    l_obj = []
    for o in objs:
        if o['UserDescription'] == user_description:
            l_obj.append(o)
    if len(l_obj) == 1:
        return l_obj[0]
    elif len(l_obj) == 0:
        raise ValueError(f'Could not find {user_description} in any UserDescription.')
    else:
        raise ValueError(f'More than one variable UserDescription {user_description}.')


obj_T24 = get_obj_from_list_by_user_description(new_wtgs_objs, 'T24')
print(obj_T24)

# Make some changes to the object before applying them with SetObjectPropsFromHandle()
# This changes the location of the object
obj.Lng = -101.40
obj.Lat = 46.75
obj.UserLabel = 'T24_new_location'
# in principle se can also change some other not so useful properties like the color
obj.Color.Red = 10
obj.Color.Green = 153
obj.Color.Blue = 25

# SetObjectPropsFromHandle() set the properties in the object given as a parameter and returns it with the new
# information.
nan_to_skipvalue(obj)
obj = objects_service.SetObjectPropsFromHandle(inObj=obj)

# WindPRO layers.
# Get information which layers are in the project.
layer_dict, layer_directories_dict = get_layer_dict(objects_service)
print('Layer names and handles')
pprint(layer_dict)
print('Layer folder names and handles')
pprint(layer_directories_dict)

# Adding new layers to the layer structure
# Adding a new layer. 0 is the base layer, so it is not placed in any folder
objects_service.AddLayer(layerName='New Layer Script Added', parentFolderHandle=0)
# Adding a new layer folder. 0 is the base layer, so the folder is visible in the base layer sturcture.
objects_service.AddLayerFolder(folderName='New layer folder', parentFolderHandle=0)
# Loading layers and layer directories again
layer_dict, layer_directories_dict = get_layer_dict(objects_service)
# Adding layer in the newly generated layer folder by name
objects_service.AddLayer(layerName='New layer in folder script added',
                         parentFolderHandle=layer_directories_dict['New layer folder'])

# set a layer to edit layer with a handle
objects_service.SetEditLayer(handle=layer_dict["WTG\\WTG's"])

# Making new objects
# AddObject(type, lat, lon, name) adds a new object of a specific type with a name and placed on parsed coordinates.
# To make the object usable in windPRO it needs additional properties set in its specific service.
new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                    lat=46.75,
                                    lng=-101.40,
                                    userDesc='New WTG obj')
handle = new_obj.Handle

# CloneObject(handle) copies an object and returns the properties as a dictionary
clone_obj = objects_service.CloneObject(handle)

# SaveObject(int handle, string path) saves an object to a file and returns true on success
file_path = os.path.join(working_dir, 'turbine.wpobjects')
objects_service.SaveObject(handle=handle, filename=file_path)

# DeleteObject(int handle) deletes an object
objects_service.DeleteObject(handle=handle)

# LoadObject() loads an object from a file and returns true on success
loadObj = objects_service.LoadObject(filename=file_path)