Writing An MDL Application To Output Custom Placemarks To Google Earth


Writing your own method to transfer MicroStation data to Google Earth is relatively easy and straightforward. Google Earth reads in data from the KML format, a variety of XML, or from the KMZ format, which is simply a zipped KML file. All that is needed to export to either format is to call mdlKml_exportToFile(), which can be found in kmlexport.fdf along with MicroStation's other functions for dealing with Google Earth. This article offers a quick introduction to using mdlKml_exportToFile().
Step One: Basic export to Google Earth

First, we will create and initialize our IKmlCoordinateRemapper object. This object is used to translate between MicroStation UORs and the WGS84 coordinates that Google Earth uses. If the remapper cannot be initialized, the design file has not been geolocated and cannot be exported to Google Earth. Thus, it is a good idea to always make initializing the remapper one of the first steps in your application.


    IKmlCoordinateRemapperP    pRemapper;
    if (SUCCESS != mdlKml_getRemapper (&pRemapper, MASTERFILE))
        return;

 

 


Next, we load the user's default Google Earth export settings and grab a handle on the user's resource file.


    KmlExportSettings  settings;
    mdlKml_getDefaultExportSettings (&settings);

    RscFileHandle              rscHandle;
    mdlResource_openFile (&rscHandle, NULL, FALSE);

 


With that done, we get the user's currently selected MicroStation view to use as our Google Earth file's default view.


    int viewToExport = mdlWindow_getSelectedViewIndex ();

 


Next, we define the filename and location for our Google Earth file. The filename used determines whether the Google Earth file is output as a compressed KMZ archive or an uncompressed KML file. KMZ is the recommended output format because the files are exponentially smaller and KMZ files can contain their associated raster data and link icons, making them more portable.


    char const*         fileName = "C:\\Example.kmz";


Finally, we define a name and description for our example file, then call mdlKml_exportToFile() with the values we have just defined. Do not concern yourself with the null value -- that is just a placeholder for the deprecated KmlExportStatistics object.


    MSWChar const*      wName    = L"Example";
    MSWChar const*      wDescr   = L"An example KMZ file.";
    mdlKml_exportToFile (fileName, wName, wDescr, NULL, MASTERFILE, &settings, pRemapper,
                         viewToExport, rscHandle);


After making this call, Example.kmz should be created and ready to go -- all that is left is to clean up after ourselves by freeing the remapper and resource file handle.


    mdlKml_freeRemapper (&pRemapper);
    mdlResource_closeFile (rscHandle);

 


Step Two: Creating a custom placemark provider

With the basics covered, we can now move on to the slightly more complex topic of creating custom Google Earth placemarks based on MicroStation element data. The IKmlElementPlacemarkProvider interface exists for this purpose. In order to use it, we only need to implement the ProvideElementPlacemark method and provide correct values for pName, pDescription, and pLocation. If any of the icon variables are left null, the default icon style will be used.

The following example implementation scans each element for Engineering Link information and if it is found, fills in the fields necessary to generate a placemark. It also uses Google Earth's capability to use custom icons for different placemarks.


/*===============================================================**//**
* @bsiclass                                    BSI           04/06
+================+==============+==============+=============+=======*/
class ExamplePlacemarkProvider : public IKmlElementPlacemarkProvider
{
public:
/*---------------------------------------------------------------**//**
* @bsimethod                                   BSI           04/06
+----------------+--------------+--------------+-------------+-------*/   
virtual bool ProvideElementPlacemark
(
MSWChar*        pName,              /* <= Name for the new placemark */
MSWChar*        pDescription,       /* <= Description for the new placemark */
DPoint3d*       pLocation,          /* <= Placemark location in UORs */
int*            pIconX,             /* <= X coordinate for placemark icon */
int*            pIconY,             /* <= Y coordinate for placemark icon */
int*            pIconWidth,         /* <= Width of placemark icon */
int*            pIconHeight,        /* <= Height of placemark icon */
double*         pIconScale,         /* <= Scale of placemark icon and its label text */
char*           pLinkIconFileName,  /* <= Location of the icon file */
MSElementDescrP pElement            /* => Current element */
)
    {
    char        description[1024], url[1024], string[4096];
    // Find an Engineering Link attached to the element.
    if (mdlTag_extractURL (url,
                           description,
                           const_cast <MSElementP> (&pElement->el),
                           (pElement->h).dgnModelRef))
        {
        // Fills in the same name and target values for the new placemark as the
        // Engineering Link.
        sprintf (string, "<a href=\"%s\">%s</a>", url, description);
        mdlCnv_convertMultibyteToUnicode (description, -1, pName, 1024);
        mdlCnv_convertMultibyteToUnicode (string, -1, pDescription, 1024);
        // Gets the element's location in UORs.
        DVector3d range;
        mdlElement_extractRange (&range, &pElement->el);
        mdlVec_addPoint (pLocation, &range.org, &range.end);
        mdlVec_scale (pLocation, pLocation, .5);
        // Helper function to acquire icon information for the new placemark.
        GetIcon (url, pIconX, pIconY, pIconWidth, pIconHeight, pIconScale,
                 pLinkIconFileName);
       
        // Return true to inform the exporter that a placemark should be generated for
        // this element.
        return true;
        }
    // Return false to inform the exporter that a placemark should not be generated for
    // this element.
    return false;
    }

/*---------------------------------------------------------------**//**
* @bsimethod                                   BSI           04/06
+----------------+--------------+--------------+-------------+-------*/   
void GetIcon
(
char*               url,                /* => Link URL */
int*                pIconX,             /* <= X coordinate for icon
                                              Distance from left most edge in pixels */                   
int*                pIconY,             /* <= Y coordinate for icon
                                              Distance from bottom most edge in pixels */
int*                pIconWidth,         /* <= Width of icon in pixels */
int*                pIconHeight,        /* <= Height of icon in pixels */
double*             pIconScale,         /* <= Scale of icon */
char*               pLinkIconFileName   /* <= Location of the icon file */
)
    {
    struct  PlacemarkIcon
        {
        char        m_extension[MAXEXTENSIONLENGTH];
        int         m_iconIndexX;       /* X index of icon from left most edge */
        int         m_iconIndexY;       /* Y index of icon from bottom most edge */
        };
    PlacemarkIcon   extensionIcons[] =
        {
        {"url", 5, 7},
        {"dgn", 0, 7},
        {"jpg", 6, 7},
        {"gif", 6, 7},
        {"tif", 6, 7},
        {"gif", 6, 7},
        {"doc", 3, 7},
        {"pdf", 2, 7},
        {"mpg", 7, 7},
        {"wav", 8, 7},
        {"mp3", 8, 7},
        {"xls", 4, 7},
        {"dwg", 1, 7},
        {"dxf", 1, 7},
        };
    int         iconIndex = 0;
    char        extension[MAXEXTENSIONLENGTH];
    extension[0] = '\0';
    mdlFile_parseName (url, NULL, NULL, NULL, extension);
   
    // Find an icon appropriate for the link's file extension.  Default to
    // "url" if extension is not found.
    if (0 != strlen (extension))
        {
        for (int i=0; i < sizeof (extensionIcons)/sizeof (PlacemarkIcon); i++)
            {
            if (0 == strcmpi (extension, extensionIcons[i].m_extension))
                {
                iconIndex = i;
                }
            }
        }
    // MicroStation's default custom placemark icons file. 
    // Distributed in Workspace\System\Image, so a relative path is OK.
    sprintf (pLinkIconFileName, "GELinkIcons.png");
    // Height and width of each icon in pixels
    *pIconWidth = 32;
    *pIconHeight = 32;
    // Translate the index of the icon to be used into pixel coordinates.
    *pIconX = extensionIcons[iconIndex].m_iconIndexX * *pIconWidth;
    *pIconY = extensionIcons[iconIndex].m_iconIndexY * *pIconHeight;
    // 1.0 is the default icon scale, but 0.7 is readable without being obnoxious.
    *pIconScale = .7;
    }
);  // ExamplePlaceMarkProvider 

 

Step Three: Tying it all together

Now that the placemark provider has been written, we can register it with the Google Earth exporter and translate our MicroStation document along with its link to KML. To do this, we need to declare a pointer to our provider, then pass that pointer to mdlKml_registerElementPlacemarkProvider() to inform the exporter that we have a callback method.


    ExamplePlacemarkProvider*   pPlacemarkProvider;
    pPlacemarkProvider = new ExamplePlacemarkProvider;
    mdlKml_registerElementPlacemarkProvider (pPlacemarkProvider);

 

 


After calling, mdlKml_exportToFile(), our placemark provider should be processed and a Google Earth file created. Once this has happened, the placemark provider should be unregistered using mdlKml_unregisterElementPlacemarkProvider().


    mdlKml_unregisterElementPlacemarkProvider (pPlacemarkProvider);
    delete pPlacemarkProvider;

 

 


The entire resulting function can be seen below:


void myGoogleEarthExport ()
    {
    IKmlCoordinateRemapperP    pRemapper;
    if (SUCCESS != mdlKml_getRemapper (&pRemapper, MASTERFILE))
        return;
    KmlExportSettings  settings;
    mdlKml_getDefaultExportSettings (&settings);
    RscFileHandle              rscHandle;
    mdlResource_openFile (&rscHandle, NULL, FALSE);
    int viewToExport = mdlWindow_getSelectedViewIndex ();    char const*         fileName = "C:\\Example.kmz";
    MSWChar const*      wName    = L"Example";
    MSWChar const*      wDescr   = L"An example KMZ file.";
    ExamplePlacemarkProvider*   pPlacemarkProvider;
    pPlacemarkProvider = new ExamplePlacemarkProvider;
    mdlKml_registerElementPlacemarkProvider (pPlacemarkProvider);
    mdlKml_exportToFile (fileName, wName, wDescr, NULL, MASTERFILE, &settings, pRemapper,
                         viewToExport, rscHandle);
    mdlKml_unregisterElementPlacemarkProvider (pPlacemarkProvider);
    delete pPlacemarkProvider;
       mdlKml_freeRemapper (&pRemapper);
    mdlResource_closeFile (rscHandle);
    }