Revit リンク

Revit リンク

Revit ドキュメントは、その他の Revit ドキュメントなどのさまざまな外部ファイルにリンクすることができます。Revit API のこれらのタイプのリンクは、RevitLinkType と RevitLinkInstance クラスによって表されます。RevitLinkType クラスは、現在の Revit ドキュメント(「ホスト」) に読み込む別の Revit ドキュメント(「リンク」)を表し、RevitLinkInstance クラスは RevitLinkType のインスタンスを表します。

新しいリンクを作成する

新しい Revit リンクを作成するには、新しい Revit リンク タイプを作成し、リンクされたドキュメントをロードする静的な RevitLinkInstance.Create()メソッドと静的な RevitLinkType.Create()メソッドを使用して、モデル内のリンクのインスタンスを配置します。RevitLinkType.Create()メソッドには、(ホストとなる)ドキュメント、リンクされるファイルへの ModelPath、RevitLinkOptions オブジェクトが必要です。RevitLinkOptions クラスは、Revit リンクの作成とロードのためのオプションを表します。オプションには、リンク ファイルとワークセットの設定に対する、相対パスまたは絶対パスを格納しているかどうかが含まれます。WorksetConfiguration クラスを使用して、リンクを作成する場合に開くワークセット(ある場合)を指定します。相対または絶対パスは、Revit がパスを保存する方法を決定しますが、Create()メソッドに渡された ModelPath は、最初にリンク済みドキュメントを検索するために完全なパスを必要とします。

次の例では、RevitLinkType.Create()の使用例を示します。変数 pathName は、リンクされるディスクのファイルに対する絶対パスです。

コード領域: 新しい Revit リンクの作成

public ElementId CreateRevitLink(Document doc, string pathName)
{
    FilePath path = new FilePath(pathName);
    RevitLinkOptions options = new RevitLinkOptions(false);
    // Create new revit link storing absolute path to file
    RevitLinkLoadResult result = RevitLinkType.Create(doc, path, options);
    return (result.ElementId);
}

RevitLinkType が作成されると、インスタンスをドキュメントに追加することができます。次の例では、RevitLinkType の 2 つのインスタンスが 100' のオフセットで追加されます。RevitLinkInstance が作成されるまで、Revit リンクが[リンクを管理]ウィンドウに表示されますが、リンク ファイルの要素は、どのビューでも表示されません。

コード領域: 新しい Revit リンク インスタンスを作成

public void CreateLinkInstances(Document doc, ElementId linkTypeId)
{
    // Create revit link instance at origin
    RevitLinkInstance.Create(doc, linkTypeId);
    RevitLinkInstance instance2 = RevitLinkInstance.Create(doc, linkTypeId);
    // Offset second instance by 100 feet
    Location location = instance2.Location;
    location.Move(new XYZ(0, -100, 0));
}
上記は、ローカル ディスクでのファイルの作業の例です。下は、Revit Server 上のモデルへのリンクを含むより複雑な例です。

コード領域: Revit Server 上のモデルへの Revit リンクを新規に作成

public static void CreateLinkToServerModel(UIApplication uiApp)
{
    UIDocument uiDoc = uiApp.ActiveUIDocument;
    Document doc = uiDoc.Document;

    // Try to get the server path for the particular model on the server
    Application application = uiApp.Application;
    String hostId = application.GetRevitServerNetworkHosts().First();
    String rootFolder = "|";
    ModelPath serverPath = FindWSAPIModelPathOnServer(application, hostId, rootFolder, "Wall pin model for updaters.rvt");

    using (Transaction t = new Transaction(doc, "Create link"))
    {
        t.Start();
        RevitLinkOptions options = new RevitLinkOptions(false);

        RevitLinkLoadResult result = RevitLinkType.Create(doc, serverPath, options);

        RevitLinkInstance.Create(doc, result.ElementId);
        t.Commit();
    }
}

private static ModelPath FindWSAPIModelPathOnServer(Application app, string hostId, string folderName, string fileName)
{
    // Connect to host to find list of available models (the "/contents" flag)
    XmlDictionaryReader reader = GetResponse(app, hostId, folderName + "/contents");
    bool found = false;

    // Look for the target model name in top level folder
    List<String> folders = new List<String>();
    while (reader.Read())
    {
        // Save a list of subfolders, if found
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "Folders")
        {
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Folders")
                    break;

                if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
                {
                    reader.Read();
                    folders.Add(reader.Value);
                }
            }
        }
        // Check for a matching model at this folder level
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "Models")
        {
            found = FindModelInServerResponseJson(reader, fileName);
            if (found)
                break;
        }
    }

    reader.Close();

    // Build the model path to match the found model on the server
    if (found)
    {
        // Server URLs use "|" for folder separation, Revit API uses "/"
        String folderNameFragment = folderName.Replace('|', '/');

        // Add trailing "/" if not present
        if (!folderNameFragment.EndsWith("/"))
            folderNameFragment += "/";

        // Build server path
        ModelPath modelPath = new ServerPath(hostId, folderNameFragment + fileName);
        return modelPath;
    }
    else
    {
        // Try subfolders
        foreach (String folder in folders)
        {
            ModelPath modelPath = FindWSAPIModelPathOnServer(app, hostId, folder, fileName);
            if (modelPath != null)
                return modelPath;
        }
    }

    return null;
}

// This string is different for each RevitServer version
private static string s_revitServerVersion = "/RevitServerAdminRESTService2014/AdminRESTService.svc/";

private static XmlDictionaryReader GetResponse(Application app, string hostId, string info)
{
    // Create request	
    WebRequest request = WebRequest.Create("http://" + hostId + s_revitServerVersion + info);
    request.Method = "GET";

    // Add the information the request needs

    request.Headers.Add("User-Name", app.Username);
    request.Headers.Add("User-Machine-Name", app.Username);
    request.Headers.Add("Operation-GUID", Guid.NewGuid().ToString());

    // Read the response
    XmlDictionaryReaderQuotas quotas =
        new XmlDictionaryReaderQuotas();
    XmlDictionaryReader jsonReader =
        JsonReaderWriterFactory.CreateJsonReader(request.GetResponse().GetResponseStream(), quotas);

    return jsonReader;
}

private static bool FindModelInServerResponseJson(XmlDictionaryReader reader, string fileName)
{
    // Read through entries in this section
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Models")
            break;

        if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
        {
            reader.Read();
            String modelName = reader.Value;
            if (modelName.Equals(fileName))
            {
                // Match found, stop looping and return
                return true;
            }
        }
    }

    return false;
}

次の例では、WorksetConfiguration が取得され、指定された 1 つのワークセットのみを開き、新しいリンクを作成する前に RevitLinkOptions に設定を戻すよう修正されています。

コード領域: 1 つのワークセットを開いた状態でリンクを作成

public bool CreateRevitLinkWithOneWorksetOpen(Document doc, string pathName, string worksetName)
{
    FilePath path = new FilePath(pathName);
    RevitLinkOptions options = new RevitLinkOptions(true);

    // Get info on all the user worksets in the project prior to opening
    IList<WorksetPreview> worksets = WorksharingUtils.GetUserWorksetInfo(path);
    IList<WorksetId> worksetIds = new List<WorksetId>();
    // Find worksetName
    foreach (WorksetPreview worksetPrev in worksets)
    {
        if (worksetPrev.Name.CompareTo(worksetName) == 0)
        {
            worksetIds.Add(worksetPrev.Id);
            break;
        }
    }

    // close all worksets but the one specified
    WorksetConfiguration worksetConfig = new WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets);
    if (worksetIds.Count > 0)
    {
        worksetConfig.Open(worksetIds);
    }
    options.SetWorksetConfiguration(worksetConfig);

    RevitLinkLoadResult result = RevitLinkType.Create(doc, path, options);
    RevitLinkType type = doc.GetElement(result.ElementId) as RevitLinkType;
    return (result.LoadResult == RevitLinkLoadResultType.LinkLoaded);
}

リンクを作成またはロードするかどうか、RevitLinkLoadResults が返されます。 このクラスには、リンクがロードされたかどうかを判断するプロパティがあります。また、作成済み、またはリンク済みモデルの ID である ElementId プロパティもあります。

リンクをロード、アンロードする

RevitLinkType には、リンクのロードに関するいくつかのメソッドがあります。Load()、LoadFrom()、Unload() により、リンクをロードまたはアンロード、または新しい位置からロードできます。これらのメソッドは、ドキュメントを再生成します。ドキュメントの[元に戻す]の履歴がこれらのメソッドによってクリアされます。明示的に起動されたすべてのトランザクション フェーズ(トランザクション、トランザクション グループ、サブトランザクションなど)は、これらのメソッドのいずれかを呼び出す前に完了する必要があります。

静的メソッド RevitLinkType.IsLoaded()は、リンクがロードされるかどうかを返します。

リンク情報を取得する

ドキュメントの各 RevitLinkType は、1 つ以上の関連付けられた RevitLinkInstances を持つことができます。RevitLinkInstance.GetLinkDocument()メソッドは、Revit リンクに関連付けられたドキュメントを返します。このドキュメントは修正できません。これはトランザクションが必要である、またはメモリでドキュメントのステータス([保存]や[閉じる]など)を修正する操作が実行できないことを意味します。

RevitLinkInstance.GetTypeId()メソッドから取得した ElementId を使用して、ドキュメントからRevitLinkInstanceの関連付けられている RevitLinkType を取得できます。リンク ファイルの RevitLinkType にはネストされたリンクに関連するいくつかのメソッドとプロパティがあります。他のドキュメントにリンクされたドキュメント自体がリンクを持っている場合があります。IsNested プロパティは、RevitLinkType がネストされたリンク(親としていくつかの他のリンクを持つ)である場合は true を、RevitLinkType がトップ レベルのリンクである場合は false を返します。メソッド GetParentId()は、このリンクの直接の親の ID を取得し、GetRootId()はこのリンクが最終的にリンクされているトップ レベルのリンクの ID を返します。どちらのメソッドも、このリンクがトップレベルのリンクの場合に invalidElementId を返します。メソッド GetChildIds()は、このメソッドに直接リンクされているすべてのリンクの要素 ID を返します。

たとえば、C がドキュメント B にリンクされ、B が ドキュメント A にリンクされている場合、C リンクに GetParentId()を呼び出すと、ドキュメント B の ID を返し、C リンクに GetRootId()を呼び出すと、ドキュメント A の ID を返します。ドキュメント A に GetChildIds()を呼び出すと、C は A 以下の直接リンクではないため、B のリンクの ID のみを返します。

RevitLinkType には、外部ファイル参照へのパスがホスト ファイルの場所(またはホストがワークシェアリングされている場合は中央モデルの場所)に対して相対的である、ディスクまたはネットワーク上の場所に対して絶対パスである、またはパスが Revit Server の場所へのパスであるかどうかを示す PathType プロパティもあります。

RevitLinkType の AttachmentType プロパティは、リンクがアタッチまたはオーバーレイかどうかを示します。「アタッチ」リンクは、その親リンクの一部とみなされ、その親が別のドキュメントにリンクされている場合はともに読み込まれます。「オーバーレイ」リンクは、その親が直接開かれている場合にのみ表示されます。

次の例では、ドキュメント内のすべての RevitLinkInstances を取得して、それらについての情報を表示します。

コード領域: リンク情報を取得

public void GetAllRevitLinkInstances(Document doc)
{
    FilteredElementCollector collector = new FilteredElementCollector(doc);
    collector.OfClass(typeof(RevitLinkInstance));
    StringBuilder linkedDocs = new StringBuilder();
    foreach (Element elem in collector)
    {
        RevitLinkInstance instance = elem as RevitLinkInstance;
        Document linkDoc = instance.GetLinkDocument();
        linkedDocs.AppendLine("FileName: " + Path.GetFileName(linkDoc.PathName));
        RevitLinkType type = doc.GetElement(instance.GetTypeId()) as RevitLinkType;
        linkedDocs.AppendLine("Is Nested: " + type.IsNestedLink.ToString());
    }

    TaskDialog.Show("Revit Links in Document", linkedDocs.ToString());
}

リンクのジオメトリ

共有座標

RevitLinkType メソッド SavePositions()および HasSaveablePositions()は、リンクされたドキュメントに共有座標に対する変更を戻して保存します。HasSaveablePositions()を使用して、リンクが保存できる位置の変更を共有したかどうかを判断します。リンク ドキュメントに対する共有座標の変更を保存するには、SavePositions()を呼び出します。SavePositions()には、Revit に修正リンクが発生した場合に状況を解決するための ISaveSharedCoordinatesCallback インタフェースのインスタンスが必要です。インタフェースの GetSaveModifiedLinksOption()メソッドは、Revit がリンクを保存するかしないか、または共有位置全体の共有を破棄するかどうかを決定します。

SavePositions()は、ドキュメントの[元に戻す]の履歴をクリアしませんが、リンクの共有座標の変更をディスクに保存するため、この動作を元に戻すことはできません。

ジオメトリ参照の変換

Reference クラスには、リンクのコンテンツのみを参照する Reference オブジェクトとホストを参照する Reference オブジェクトを相互に変換できるリンク ファイルに関連するメンバーがあります。たとえば、これを使用することで、アプリケーションはリンクでジオメトリを確認したり、必要な面を検索したり、その面への参照を面ベースのインスタンスの配置に適したホストの参照に変換します。また、これらの Reference のメンバーを使用して、(寸法またはファミリなどから)ホストの参照を取得し、これをElement.GetGeometryObjectFromReference()での使用に適したリンクの参照に変換することができます。

参照。LinkedElementId プロパティは、この参照によって参照されるリンク ドキュメントのトップレベルの要素の ID、またはリンクされた RVT ファイルの要素を参照しない参照の InvalidElementId を表します。参照。CreateLinkReference()メソッドは、RevitLinkInstance を使用して Revit リンクの参照から参照を作成します。また、参照。CreateReferenceInLink()メソッドは、ホスト ファイルの参照から Revit リンクの参照を作成します

リンクの要素を選択する

選択メソッドである PickObject()と PickObjects()を使用して、Revit リンクのオブジェクトを選択できます。リンク ファイルの要素を選択するには、PickObject()または PickObjects()の最初のパラメータに ObjectType.LinkedElement の列挙値を使用します。このオプションでは、リンクの要素のみを選択でき、ホスト ドキュメントの要素は選択できません。

下の例では、ISelectionFilter を使用して、リンク ファイルの選択された壁のみを許可しています。

コード領域: リンク ファイルの要素を選択

public void SelectElementsInLinkedDoc(Autodesk.Revit.DB.Document document)
{
    UIDocument uidoc = new UIDocument(document);
    Selection choices = uidoc.Selection;
    // Pick one wall from Revit link
    WallInLinkSelectionFilter wallFilter = new WallInLinkSelectionFilter();
    Reference elementRef = choices.PickObject(ObjectType.LinkedElement, wallFilter, "Select a wall in a linked document");
    if (elementRef != null)
    {
        TaskDialog.Show("Revit", "Element from link document selected.");
    }
}

// This filter allows selection of only a certain element type in a link instance.
class WallInLinkSelectionFilter : ISelectionFilter
{
    private RevitLinkInstance m_currentInstance = null;

    public bool AllowElement(Element e)
    {
        // Accept any link instance, and save the handle for use in AllowReference()
        m_currentInstance = e as RevitLinkInstance;
        return (m_currentInstance != null);
    }

    public bool AllowReference(Reference refer, XYZ point)
    {
        if (m_currentInstance == null)
            return false;

        // Get the handle to the element in the link
        Document linkedDoc = m_currentInstance.GetLinkDocument();
        Element elem = linkedDoc.GetElement(refer.LinkedElementId);

        // Accept the selection if the element exists and is of the correct type
        return elem != null && elem is Wall;
    }
}