Export all components of given type from manifold map file

You may sometime need to export all the components of given type so they can be used in another GIS environment. Here goes a script that does exactly this. The example exports all the drawings and tables to either shp or xls:

using Manifold.Interop.Scripts;
using M = Manifold.Interop;

class Script {
	static void Main() {
		//Some setup stuff first
        string exportFld = "H:\\ExportedComponents\\";

        M.Application mApp = Context.Application;
        M.Document mDoc = (M.Document)mApp.ActiveDocument;
        M.ComponentSet mCompSet = mDoc.ComponentSet;

        //Exporters
        M.ExportShp shpExporter = (M.ExportShp)mApp.NewExport("SHP");
        shpExporter.ConvertPolicy = M.ConvertPolicy.ConvertAll;

        M.ExportXls xlsExporter = (M.ExportXls)mApp.NewExport("XLS");
        xlsExporter.ConvertPolicy = M.ConvertPolicy.ConvertAll;

       //iterate through all components
        for (int i = 0; i < mCompSet.Count; i++ )
        {
            mApp.StatusText = "Reading component " + i.ToString() + " of " + mCompSet.Count;

            switch (mCompSet[i].Type)
            {
                case M.ComponentType.ComponentDrawing:

                    shpExporter.Export(mCompSet[i], exportFld + mCompSet[i].Name + shpExporter.DefaultExtension, M.ConvertPrompt.PromptNone);

                    break;

                case M.ComponentType.ComponentTable:

                    xlsExporter.Export(mCompSet[i], exportFld + mCompSet[i].Name + xlsExporter.DefaultExtension, M.ConvertPrompt.PromptNone);
                    break;
            }
        }

        mApp.StatusText = "";
        mApp.MessageBox("Done!", "Info");
	}
}

Creating a cost surface in Manifold

Recently I have played a bit with manifold to find out how to prepare a cost surface. There are no out of the box tools in manifold to magically create such a surface but a bit of sql does the job.

Lets assume we have two components - a surface and a drawing. Surface will be a container for our output data and the drawing will provide the input - in this case a bunch of simple lines imitating a road or stream network.

Lets start with preparing an output surface - simply copy the drawing and paste it as a surface and when done run an update query to set the cell values to 0:

UPDATE [Surface] SET [Height (I)] = 0

When our surface is ready we can start playing with generating a cost surface (in this case this will be a distance from the nearest vector object):

UPDATE
(SELECT
	[Surface].[Height (I)],
	Distance(
		AssignCoordsys(
			NewPoint([Center X (I)],[Center Y (I)]),
			CoordSys("Surface" AS COMPONENT)
		),
		Line.[Geom]
	) AS [Distance]
FROM
	[Surface],
	(SELECT AllBranches([Drawing].[Geom (I)]) As [Geom]
	FROM [Drawing]) AS Line)
SET [Height (I)] = [Distance];

 After running this query our surface should look like this:

So far s good but let's have a look at raster reclassification to make the output a bit more useful for further usage. We can write another query that will update our already generated surface, though for this example I have decided to combine it with the query above:

UPDATE
(SELECT
	[Surface].[Height (I)],
	Distance(
		AssignCoordsys(
			NewPoint([Center X (I)],[Center Y (I)]),
			CoordSys("Surface" AS COMPONENT)
		),
		Line.[Geom]
	) AS [Distance]
FROM
	[Surface],
	(SELECT AllBranches([Drawing].[Geom (I)]) As [Geom]
	FROM [Drawing]) AS Line)
SET [Height (I)] = 
	CASE
		WHEN  [Distance] < 5 THEN 0
		WHEN  [Distance] < 10 THEN 1
		WHEN  [Distance] < 15 THEN 2
		WHEN  [Distance] < 20 THEN 3
		WHEN  [Distance] < 25 THEN 4
		WHEN  [Distance] < 30 THEN 5
		WHEN  [Distance] < 35 THEN 6
		WHEN  [Distance] < 40 THEN 7
		WHEN  [Distance] < 45 THEN 8
		ELSE 9
	END

 So now our surface looks like this:

What's next? Having prepared more surfaces for analysis one can now combine them using surface tools. If surface tools are not avaialble it is easy enough to combine surfaces using the UPDATE technique shown above.

If you want to play a bit with the examples shown here just download this file: example.map (1.00 mb)

Cutting image from a bigger raster source

This script has already been posted on the georeference.org (http://forum.manifold.net/forum/t99935.7) so nothing new will be presented...

There was one interesting thing though - how to get from a bbox of a geom used by the input drawing to the actual size of the output image in pixels. The script needed to cut images and preserve their actual resolution - as one would crop an image in photoshop.

First I had to prepare a coordinate converter and to grab some data off the image coordinate system needed later for calculating the actual size of a cut image in pixels:

//prepare coordinate converter in order to properly calculate image extent in pixels later
Manifold.Interop.CoordinateConverter coordConverter = manApp.NewCoordinateConverter();
coordConverter.Prepare((Manifold.Interop.Base)map.CoordinateSystem, (Manifold.Interop.Base)inputImage.CoordinateSystem);

//also grab the input image local scales
double imageLocalScaleX = inputImage.CoordinateSystem.ParameterSet["localScaleX"].Value;
double imageLocalScaleY = inputImage.CoordinateSystem.ParameterSet["localScaleY"].Value;

The next step was to grab the bounding box of a source geometry used to cut a new image from the source raster:

//grab the bounding box of an object
Manifold.Interop.Rect geomBbox = geomSet.get_Item(n).Box;

After that I used the bottom left and top right corners of the bbox and coverted them to the source image coordsys in order to calculate the size of the new image in pixels:

//get the corner points of the geom's bbox
Manifold.Interop.Point bottomLeft = manApp.NewPoint(geomBbox.XMin, geomBbox.YMin);
Manifold.Interop.Point topRight = manApp.NewPoint(geomBbox.XMax, geomBbox.YMax);

//convert them to the image coordsys
coordConverter.Convert((Manifold.Interop.Base)bottomLeft, null);
coordConverter.Convert((Manifold.Interop.Base)topRight, null);

//image size in pixels
int imageSizeX = (int)((topRight.X - bottomLeft.X) / imageLocalScaleX);
int imageSizeY = (int)((topRight.Y - bottomLeft.Y) / imageLocalScaleY);

//and then cut tile
map.RenderAreaTo(fileName, imageSizeX, imageSizeY, geomBbox, true);

Fairly straight forward isn't it?

Anyway, if you would like to use this script it is attached below. There are some input params and they need to be set prior to running the script. The reason behind using a RenderAreaTo method of a map object instead of the image object is explained in the script.

Although perhaps it would be easier to use gdal for the task than writing a script this exercise seemed to be interesting enough to give it a go. Having a GUI environment to choose areas of interest by simply drawing a rectangle over the image is a good reason isn't it ;-) Make sure though you switch off the input drawing before rendering the new images...

Also bear in mind that if you work with a high resolution ecw for example, the image you want to cut may be quite large since and the script calculates its size based on the source image resolution - to make it simpler: trying to cut a too big image out of a high res source image may make your pc unresponsive for a longer time ;-)

EDIT: I would almost forget - the input drawing is expected to have a column with names for the new images.

tileCutter.cs (7.53 kb)

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;
    }  
}