Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Importing and Displaying Satellite Images

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
harmvandenbrand
1778 Views, 11 Replies

Importing and Displaying Satellite Images

Hello all,

 

I'm building an add-in for Revit and I would like to be able to import and display third-party satellite imagery in order to place buildings in their 'real' position. I would like to be able to do this in a 3D view, but I don't know how.

 

The user workflow for my add-in is this:

- A user opens the add-in and is prompted to input a location through a WPF window.

- Once a location is confirmed, a number of things are created/imported into the active project to make it look as close as possible to its actual real-life location. One of these things is the satellite image I'm seeking to import here.

 

Essentially, my question is exactly this one, but instead doing that programmatically/automatically through an add-in. In that thread, a suggestion is made to create a decal with the desired image, but this does not seem to be supported through the API.

Another approach I found is to use PostCommands to create and place decals, but these commands are apparently only executed after exiting the API context and only one at a time. As my add-in aims to perform a whole bunch of functionalities in one go, this seem ill-suited for my use case. It seems to be possible to chain a bunch of PostCommands, but this is a little 'hacky' and not recommended, especially for commercial use.

 

Am I overlooking some existing functionality? Is my use case just not supported in current Revit? I'm new to programming for Revit, so it's very possible I've missed something.

 

I'm running / programming for Revit 2019 on Windows 10.

 

Thank you all for your time,

 

Harm

11 REPLIES 11
Message 2 of 12

Dear Harm,

 

Thank you for the interesting question and explanation, and all the research you already put into it.

 

You have proceeded exactly as I would suggest, and the finding are what I would have expected.

 

I am sorry to say that I have nothing to add from my point of view.

 

I wish you good luck in putting together a viable and sufficiently robust workflow for your use case.

 

Best regards,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 12

Hi,

 

what about creating a new material, setting its texture path - and then, making a TopoSurface and assigning the material to it?

 

https://thebuildingcoder.typepad.com/blog/2017/11/modifying-material-visual-appearance.html

 

I don't know how to adjust the UV mapping for the TopoSurface, but it it worked, you would see your satellite image in 3D.

 

Revitalizer




Rudolf Honke
Software Developer
Mensch und Maschine





Message 4 of 12

Thanks to all for the replies! It took some time to try out the proposed solution (accessing AppearanceElements is convoluted!), so that's why it took me this long to reply.

 

In the end, though I had to work around some weird quirks with the API, adding the image as a texture to a topography through a material works great.

 

Thanks again for the suggestion, I couldn't have made it work without it.

Message 5 of 12

Wow, congratulations!

 

Can you share some of the quirks and weirdnesses you ran into, and how you handled them?

 

The entire solution might be of great interest to others as well, in fact...

 

Have a nice Sunday and good health to all!

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 6 of 12

Hi Harmvandenbrand,

 

I am also trying to achieve same functionality and didn't find API to achieve the same.

If you can share the solution used to achieve the output, that will be great help.

Thanks and Advance.

 

 

Message 7 of 12

Hi!

 

My apologies for the late reply, both to Tamesh and especially to Jeremy 😅.

 

As mentioned, I ended up taking Revitalizer's suggested approach of creating a new material and setting its texture.

 

Material underlayMaterial = Material.Create(revitDocument, materialName);

 

 

To this material, I link a so-called AppearanceAsset:

 

underlayMaterial.AppearanceAssetId = assetElement.Id;

 

(more on how I get this assetElement in a moment)

 

And then I assign the path of a jpeg image to the texture asset:

 

using (AppearanceAssetEditScope editScope = new AppearanceAssetEditScope(revitDocument))
{
    Asset editableAsset = editScope.Start(assetElement.Id);

    AssetProperty assetProperty = editableAsset.FindByName("generic_diffuse");
    Asset connectedAsset = assetProperty.GetConnectedProperty(0) as Asset;

    //Edit bitmap
    if (connectedAsset.Name == "UnifiedBitmapSchema")
    {
        AssetPropertyString path = connectedAsset.FindByName(UnifiedBitmap.UnifiedbitmapBitmap) as AssetPropertyString;

        if (path.IsValidValue(imagePath))
            path.Value = imagePath;

        //You might have to fiddle a bit with the scale properties, for example when your source uses centimeters:
        AssetPropertyDistance scaleX = connectedAsset.FindByName(UnifiedBitmap.TextureRealWorldScaleX) as AssetPropertyDistance;
        AssetPropertyDistance scaleY = connectedAsset.FindByName(UnifiedBitmap.TextureRealWorldScaleY) as AssetPropertyDistance;

        //Because newly added bitmaps are displayed in inches.
        if (scaleX.DisplayUnitType == DisplayUnitType.DUT_DECIMAL_INCHES)
            scaleX.Value /= 2.54;
        if (scaleY.DisplayUnitType == DisplayUnitType.DUT_DECIMAL_INCHES)
            scaleY.Value /= 2.54;

    }
    editScope.Commit(true);
}

 

 

Now, the reason why I would call my solution 'hacky', is because of how I retrieve the assetElement.

 

Instinctively, I would want to create a new, empty instance of Asset. Something like AppearanceAssetElement assetElement = AppearanceAssetElement.Create();

However, this is not how Revit's material/texture api works. We can only use those materials/textures/etc. that are present in Revit's libraries. Therefore we can only make copies of existing ones:

 

//Retrieve asset library from the application (this is the only source available; instantiating from zero is impossible)
IList<Asset> assetList = commandData.Application.Application.GetAssets(AssetType.Appearance);

//Select arbitrary asset from library (200 works, not all do)
Asset asset = assetList[200];

AppearanceAssetElement assetElement;
try
{
    assetElement = AppearanceAssetElement.Create(revitDocument, someNewName, asset);
}

 

Yes, I really just randomly tried indices in that assetList until I found one that worked, and hardcoded that one in. Not all AppearanceAssets in the list have the necessary "generic_diffuse" assetProperty to which we can bind a texture, so we have to select one that does.

If you are developing your addin for external parties this is risky, because we can't ensure that the same libraries are available for any particular user. It's probably best to somehow filter for valid AppearanceAssets.

 

Also, you can see that retrieving this appearanceAsset requires ExternalCommandData (which I named CommandData in the code given), which an addin retrieves via the 'Execute' method of a IExternalCommand-implementing class.

 

Also, remember to wrap most of these snippets in transactions.

 

I hope this helps!

Message 8 of 12

Thank you very much for sharing your nice solution!

 

I have asked the development team whether the hacky part can be cleaned up somehow.

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 9 of 12

Thank you again for the nice implementation and explanation. Preserved for posterity:

 

https://thebuildingcoder.typepad.com/blog/2021/12/symbol-instance-material-data-journal-break.html#3

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 10 of 12

You can find an asset that contains a certain named property via Linq e.g.:

 

Dim Assets As List(Of Visual.Asset) = app.Application.GetAssets(Visual.AssetType.Appearance)
'Find an asset with a property matching Visual.Generic.GenericDiffuse
Dim J0 = Assets.FirstOrDefault(Function(x) x.FindByName(Visual.Generic.GenericDiffuse) IsNot Nothing)

 

This seems better than a fixed index since you don't know for sure how that number may change e.g. if user manually adds an asset etc.

 

Obviously the Assets library is quite large to search but there are existing Materials and Assets in the document which can be duplicated instead. So it is often better to know what you are looking for e.g. load a Family with your known material and the Asset you want to manipulate (if you can't find it).  Most materials will have the appearance asset with the bitmap property though.

 

There is an example of this within RevitAPI.chm under AppearanceAssetElement.Duplicate. As noted if it isn't there in the document then it is straightforward to load something with it to add it.

Message 11 of 12

Dear Harm, 

 

here is the devteam response:

 

"An asset is a resource with economic value that an individual, corporation or country owns or controls with the expectation that it will provide a future benefit."

In Revit, an (material) asset is owned by the libraries too, but currently the libraries are only data but not classes in code, so there's no way to create an asset from the libraries but can only duplicate one from existing.
 
The user's problems is trying to find an asset that can assign an image; I suggest below steps:
 
  1. Duplicate a new asset (or material) from any existing ones that serves only for this purpose.
  2. If the new asset contains "unified_bitmap", then change it as desired.
  3. If not, then create a connected asset with AssetProperty.AddConnectedAsset() that should contains "unified_bitmap" in the schema.
At last, exposing decal element to API should be the best way. (There may also contains some workaround or hacky way through external file mechanism, but I cannot confirm that.)
  
Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 12 of 12

Hi Jeremy,

 

Thanks for the update. If I understand correctly, my solution follows steps 1 and 2 of the dev-team's suggestion. So it's good to know I'm not doing anything too weird. Except for selecting a hardcoded element from the library by hand, but I'll happily use @RPTHOMAS108 's suggestion of selecting one through list filtering. It'll prevent any errors that would occur if the asset libraries ever were to change.

Always good to go through old code again and find yourself thinking "this could be improved".

 

I'm satisfied with how I currently create/copy an asset (again, step 1 and 2). It's just that when I first started digging through the api to get to the functionality I needed, it was a bit like groping around in the dark. My first instinct when it came to the AppearanceAsset part of the problem, at the time, was to instantiate a new one. It wasn't immediately clear to me that I couldn't do that, but that I could duplicate one. I learn more with every feature I implement 😁.

 

That having been said, things like our blog and the documentation at revitapidocs have helped out immensely, with this problem and with others, so thank you very much!

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Customer Advisory Groups


Rail Community