Preparing the data description for the metadata docu

Today I had to prepare a description of all the vector drawings I had in a project in a semi tabular form listing a folder, contained drawings and their column, column types and the length of the data in each column. In other words I needed to create a dataset description for the metadata document needed in my current project.

Obviously collecting such data for a drawing or two is fairly quick but manually fetching data for multiple components would a pain in the neck…

A script seemed to be the best option here so I decided to write one ;-) It searches for the drawings contained in folders (one level of nesting) and then lists the needed stuff. It can easily be adapted to list drawings in a root folder as well and to dig deeper in nested folders. It can also be adjusted to look for other types of components or to grab some info about the coordsystems used by them - I just needed the drawings though hence a rather simplistic version of the script was enough.

Anyways, feel free to use it if you like (simply add a c# script and replace its content with the attached). Bear in mind script lists info only for drawings contained in a folder.

Output example:

Topo200k
========================================

Drogi
----------------------------------------
ColumnName	DataType	FieldLength
ID	ColumnTypeInt32U	4
LENGTH	ColumnTypeFloat64	8
KLASA	ColumnTypeInt16	2
ID 2	ColumnTypeInt32	4
NUMER	ColumnTypeAText	16
----------------------------------------

Duze_rzeki_i_jeziora
----------------------------------------
ColumnName	DataType	FieldLength
ID	ColumnTypeInt32U	4
NAME	ColumnTypeAText	100
SHAPE_LENG	ColumnTypeFloat64	8
SHAPE_AREA	ColumnTypeFloat64	8
TOPO_CLASS	ColumnTypeInt32	4
----------------------------------------

 And the script:

using Manifold.Interop.Scripts;
using System;

class Script {
	static void Main() {
		pullDataDescription();
	}

    static void pullDataDescription()
    {
        //reference the app object first
        Manifold.Interop.Application manApp = new Manifold.Interop.Application();

        //grab doc object
        Manifold.Interop.Document manDoc = (Manifold.Interop.Document) manApp.ActiveDocument;

        //create output comments component
        Manifold.Interop.Comments cmt = manDoc.NewComments("Drawings&Data", false);
        cmt.Folder = null; //so it's always in the root folder

        //record the time this summary was created
        DateTime date = DateTime.Now;
        cmt.AddText("Report generated " + date.ToLongDateString() + " " + date.ToLongTimeString() + Environment.NewLine);
        cmt.AddText(writeBreakLine("*", 40) + Environment.NewLine);

        //iterate through components
        foreach (Manifold.Interop.Component cmp in manDoc.ComponentSet)
        {
            //check if this is a folder
            if (cmp.Type == Manifold.Interop.ComponentType.ComponentFolder)
            {
                Manifold.Interop.Folder fld = (Manifold.Interop.Folder)cmp;
                
                //write the folder name
                cmt.AddText(fld.Name + Environment.NewLine);
                cmt.AddText(writeBreakLine("=", 40) + Environment.NewLine);

                //iterate through drawings
                foreach (Manifold.Interop.Component fldCmp in fld.Children)
                {
                    //check if this is a drawing
                    if (fldCmp.Type == Manifold.Interop.ComponentType.ComponentDrawing)
                    {

                        Manifold.Interop.Drawing drw = (Manifold.Interop.Drawing)fldCmp; 

                        //write drawing name
                        cmt.AddText(drw.Name + Environment.NewLine);
                        cmt.AddText(writeBreakLine("-", 40));

                        //write headers for the tables
                        cmt.AddText("ColumnName" + "\t" + "DataType" + "\t" + "FieldLength" + Environment.NewLine);
                        //iterate through colums
                        foreach (Manifold.Interop.Column col in ((Manifold.Interop.Table)drw.OwnedTable).ColumnSet)
                        {
                            if (col.Category == (int)Manifold.Interop.ColumnCategory.ColumnCategoryNative)
                            {
                                cmt.AddText(col.Name + "\t" + (Manifold.Interop.ColumnType)col.get_Type()  + "\t" + col.Size + Environment.NewLine);
                            }
                        }
                        cmt.AddText(writeBreakLine("-", 40) +    Environment.NewLine);
                    }
                }
                //write the folder name
                cmt.AddText(writeBreakLine("=", 40));
                cmt.AddText("eof " + fld.Name + Environment.NewLine + Environment.NewLine + Environment.NewLine);
            }
        }
        //open the comments component
        cmt.Open();
    }

    //writes a 'break line'
    static string writeBreakLine(string character, int length)
    {
        string output = string.Empty;
        for (int n = 0; n < length; n++)
        {
            output += character;
        }
        output += Environment.NewLine;
        return output;
    }  
}

Decimal degrees do dd°mm'ss"

A quick task - convert decimal degrees back to dd°mm'ss''. Not sure if there are other ways of doing this in manifold at this stage so an active column did the trick.

A script is in c#, so just copy and paste it to the script attached to a table when creating an active column. Then simply put the appropriate method name to be used by a column and that's basically it.

 

using Manifold.Interop.Scripts;
using System;

class Script {

	static string Decimal2Degrees(double decimalDegrees)
	{
		
		int fullDegrees = (int) Math.Floor(decimalDegrees);
		
		double decimalMinutes = (decimalDegrees - fullDegrees) * 60;
		int fullMinutes = (int) Math.Floor(decimalMinutes);

		double decimalSeconds = (decimalMinutes - fullMinutes) * 60;
		int fullSeconds = (int) Math.Round(decimalSeconds);

		string strMinutes = fullMinutes.ToString();
		if(fullMinutes < 10){
			strMinutes = "0" + fullMinutes.ToString();
		}

		string strSeconds = fullSeconds.ToString();
		if(fullSeconds < 10){
			strSeconds = "0" + fullSeconds.ToString();
		}

		return fullDegrees.ToString() + "°" + strMinutes + "'" + strSeconds + "\"";
	}

	static object getLongitude()
	{
		return Decimal2Degrees((double)Context.Record.get_Data("Longitude (I)"));
	}

	static object getLatitude()
	{
		return Decimal2Degrees((double)Context.Record.get_Data("Latitude (I)"));
	}
}

[EDIT] As Adam has pointed out, the code in its first form could return 60 for seconds what of course does not make too much sense. Now, there are a few ways of fixing it - one can use floor and drop the remaining decimal part, or a test can be performed to check if the number of seconds does not exceed 59 seconds, or maybe if the number of seconds rounds to 60 one should increase number of minutes by one and then degrees by one if minutes reach 60 and... Well, to keep it simple at this stage one line of the code needs to be added just after assigning a value to the fullSeconds variable:

fullSeconds = fullSeconds > 59? 59: fullSeconds;

This works as if one used Math.Floor but only if the number rounds to 60. Not perfect for all uses but...

One more thing - as I started thinking a bit more about what the code actually does it appeared to me that for negative coordinates it will return wrong values as it uses Math.Floor - so for -37.50 one will get -38... Living on the northern hemisphere east of Greenwich and not having to deal with negative coordinates on a daily basis, huh?