Wednesday, 9 March 2016

LV95 Reference frame - world files for raster

If you need to transform world files as part of the the reference frame change (LV03 --> LV95) - I have written a small tool which does the transformation, either by performing a transformation using REFRAME or by performaning a simple translation. Let me know if the tool would be helpful - I can send it by email.



Oracle - ORDER BY for numeric values in VARCHAR2 field

We have an address list where the housenumber is stored as VARCHAR2. The house number is not always a number. Here a few examples:

---
  21-25   
 54-58, 66-70, 76-78
1
...
10
10a-10b
10b
102
102-104
10-34
...
12 / 14
...
128
1+3
...
2
2 - 10
2, 4, 6
2a-2c
2b / 2c
20

What all data sets have in common is that the first characters are always digits. To order all data sets numerically using the housenumber the following SQL can be used

select strassennr, 
from wfe_sk_einsatzplan 
order by  lpad(regexp_substr(strassennr,'(([0-9]+))'),20);

Which returns a list like:

1
1 - 49
...
1-19
1+3
1
1-7
1
1
1-5
1
...
1
1-7
1-11
1-3
...
1-3
1-7
1
1+3a
2, 4, 6
2
2b / 2c
2
2-12
2-12
2
2-4
2-12
2
2+8
..
2-6, 7-23
2-6c
..
2a-2c
...

REGEXP_SUBSTR extracts the first numeric characters we need for sorting. As the result is still of type VARCHAR2 LPAD makes sure, that they are ordered accordingly (see here).

Alternative: to_number(regexp_substr(strassennr,'(([0-9]+))'));

The report now shows the datasets in an order the user expects:
datasets ordered by house number

Monday, 1 February 2016

Map-Plot - batch plotting

If you use Map plot module (Industry Model extension) you can generate maps in a batch process.
Unfortunately we always get an unspecific error message when executing the batch plot module - something along the lines of 

"Plot was not configured correctly"

Funny though as you can generate the preview and plot the preview without any issue.

To get our 400+ maps plotted I wrote a quick'ndirty batch plot plugin myself. It does its job - maybe it is useful for someone else. Most bits are taken from Map development sample or through-the-interface blog. I also copied some bits from another older plugin - that's why a class is called "Inselplan" but has nothing to with it.

Some notes:

- on one PC generating a PLT file took roughly 1 minute per map, on a second PC roughly 5 minutes per map. Both PCs have nearly the same specs and software installed. 
- there is an issue with FDO hatches - if the viewport is rotated some hatches (it seems most hatches except solid) have artifacts (such as a white rectangular or hatches occuring twice but slightly shifted).


Hatch issue - FDO hatches, viewport rotated
Map 2013, SP2

source code:


 using System;  
 using Autodesk.Map.IM.Forms;  
 using Autodesk.Map.IM.Plot.Application;  
 using Autodesk.Map.IM.Plot.Components.PlotLibrary;  
 using Autodesk.Map.IM.Plot.Components.PlotLibrary.ContextMenu;  
 using Autodesk.Map.IM.Plot.Data;  
 using Autodesk.Map.IM.Plot.Components.PlotLibrary.Command;  
 namespace Inselplan_Batch  
 {  
   /// <summary>  
   /// <para>  
   /// Our document plugin - its priority has to be set so it will be loaded after the main plot  
   /// plugin - see this example's TBP file.   
   /// </para>  
   /// <para>  
   /// </para>  
   /// </summary>  
   public class DocumentPlugin : DocumentPlugIn  
   {  
     /// <summary>  
     /// Called by the TB Forms framework - we will use it to initialize our plugin  
     /// </summary>  
     /// <param name="sender"></param>  
     /// <param name="e"></param>  
     public override void OnInitComplete(object sender, EventArgs e)  
     {  
       base.OnInitComplete(sender, e);  
       //get the [PlotFlyIn] for our TB document...  
       PlotFlyIn currentPlotFlyIn = PlotFlyIn.GetCurrent(this.Document);  
       //...which can be [null], if it has been excluded in TB Admin  
       if (null != currentPlotFlyIn)  
       {  
         //connect to the [PlotExtensionInitialized] event - it will be raied by the respective  
         //flyin after the library (TreeView) has been fully loaded in response to  
         //the [WorkspaceOpened] event - now the Plot API can be used  
         currentPlotFlyIn.PlotExtensionInitialized += this.HandlePlotExtensionInitialized;  
       }  
     }  
     private void HandlePlotExtensionInitialized(object sender, EventArgs e)  
     {  
       //get the [PlotFlyIn]...  
       PlotFlyIn plotFlyIn = (PlotFlyIn)sender;  
       //...and the PltPlotLibrary (which is a TreeView)...  
       PltPlotLibrary plotLibrary = plotFlyIn.PltPlotLibrary;  
       //...so we can add our menu entries to the the plot-specific context menu via the library's [PltContextMenuFactory]  
       PltContextMenuFactory contextMenuFactory = plotLibrary.ContextMenuFactory;  
       //That is, it won't be available for templates or groups  
       //context menus are available for the following types:  
       // - IPlot  
       // - IPlotTemplate  
       // - IPlotGroup  
       // - IPlotTemplateGroup  
       // nicht auf Template setzen, der neue Plot wird in der Gruppe gespeichert in der Befehl aufgerufen wird  
       // - das geht aber nicht bei Templates und führt zum Absturz  
       PltCommonContextMenuStrip contextMenuStrip = contextMenuFactory.GetMenuForLibraryType(typeof(IPlotGroup));  
       //every command has its key - the same command (class) key is not allowed to be added  
       //twice to the same context menu  
       string editCommandKey = PltCommand.GetDefaultCommandKey(typeof(PltEditCommand));  
       //the plot context menu contains the [PltEditCommand] command. Get its  
       //insertion index...  
       int editCommandIndex = contextMenuStrip.Items.IndexOfKey(editCommandKey);  
       //...so we can then add our own commands right behind it  
       contextMenuStrip.InsertMenuItem(new Inselplanerstellung(plotLibrary), editCommandIndex + 1);  
       //contextMenuStrip.InsertMenuItem(new MyFastEditCommand2(plotLibrary), editCommandIndex + 2);  
       //alternatively, one can also use the [AddMenuItem] method which adds the command  
       //as the last item - retrieving another command's key + index first is not required then  
       //  
       //contextMenuStrip.AddMenuItem(new MyFastEditCommand2(this.Document, plotLibrary));  
     }  
   }  
 }  
 
 using Inselplan_Batch.Properties;  
 using Autodesk.Map.IM.Plot.Api;  
 using Autodesk.Map.IM.Plot.Components.PlotLibrary;  
 using Autodesk.Map.IM.Plot.Components.PlotLibrary.Command;  
 using Autodesk.Map.IM.Plot.Components.Runtime;  
 using Autodesk.Map.IM.Plot.Components;  
 using Autodesk.Map.IM.Plot.Data;  
 using Autodesk.Map.IM.Data;  
 using Autodesk.Map.IM.PlaneGeometry;  
 using System.Windows.Forms;  
 using System.Collections;  
 using System.Collections.Generic;  
 using Autodesk.AutoCAD.Runtime;  
 using Autodesk.AutoCAD.EditorInput;  
 using Autodesk.AutoCAD.ApplicationServices;  
 using Autodesk.AutoCAD.DatabaseServices;  
 using Autodesk.AutoCAD.PlottingServices;  
 namespace Inselplan_Batch  
 {  
   /// <summary>  
   /// </summary>  
   /// <remarks>  
   /// </remarks>  
   [PltPrerequisiteSingleNodeSelected]  
   public class Inselplanerstellung : PltCommand  
   {  
     /// <summary>  
     /// Ctor - it requires the caller to pass a valid <see cref="PltPlotLibrary"/> instance.  
     /// </summary>  
     /// <param name="library"></param>  
     public Inselplanerstellung(PltPlotLibrary library)  
       : base(library, Resources.command)  
     {  
     }  
     /// <summary>  
     /// Called by the Plot API after the user has selected our context menu entry.  
     /// </summary>  
     protected override void ExecuteCommand()  
     {  
       try  
       {  
         //welche gruppe wurde ausgewhlt?  
         IPlotLibraryGroup groupP = this.GetGroup(this.GetFirstTreeNode(this.PlotLibrary.SelectedItems));  
         //PlotLibrary ist die grafische Repräsentation - das Plot-Flyin/Baumstruktur  
         IPlotConnection plotConnection = this.PlotLibrary.Connection;  
         IPlotLibrary myLibrary = plotConnection.Library;  
         //PltPlotLibrary myPltPlotLibrary = this.PlotLibrary;  
         // Zum Speichern der neuen Plots wird die gruppe verwendet,  
         // aus der der Befehl heraus aufgerufen wurde.  
         //Gruppe also vorher manuell anlegen  
         IPlotLibraryGroup group = this.GetGroup(this.GetFirstTreeNode(this.PlotLibrary.SelectedItems));  
         IPlotGroup plotGroup = group as IPlotGroup;  
         //wird benötigt zum Pdate von PLT_PLOT  
         TBConnection myConnection = plotConnection.Connection;  
         //die beiden Views  
         FeatureClass tabPLT_PLOT_GROUP_PLOT;  
         string tabName_PLT_Plot_GROUP_PLOT = "PLT_PLOT_GROUP_PLOT";  
         string PlotTabelle = "PLT_PLOT";  
         Autodesk.Map.IM.Data.FeatureClass PlotFeatureClass = myConnection.FeatureClasses[PlotTabelle];  
         tabPLT_PLOT_GROUP_PLOT = myConnection.FeatureClasses[tabName_PLT_Plot_GROUP_PLOT];  
          //FID der Plotgruppe?  
         string SQLAbfrage = "select fid from plt_plot_group where group_name = '" + groupP.Name + "'";  
         //this.Application.MessageBox(SQLAbfrage, "Bericht - Hinweis1");  
         Autodesk.Map.IM.Data.Tools.RecordReader reader = new Autodesk.Map.IM.Data.Tools.RecordReader(myConnection);  
         reader.Open(SQLAbfrage);  
         reader.Read();  
         long? FIDPlotGruppe = reader.LngValue("FID");  
         reader.Close();  
         //FID der Plotgruppe, in die geplottet Plots verschoben werden sollen          
         string SQLAbfrage2 = "select fid from plt_plot_group where group_name = 'Grundbuchpläne_geplottet'";  
         //this.Application.MessageBox(SQLAbfrage, "Bericht - Hinweis1");  
         Autodesk.Map.IM.Data.Tools.RecordReader reader2 = new Autodesk.Map.IM.Data.Tools.RecordReader(myConnection);  
         reader.Open(SQLAbfrage2);  
         reader.Read();  
         long? FIDPlotGruppeVerschieben = reader.LngValue("FID");  
         reader.Close();  
         if (FIDPlotGruppe == null)  
         {  
           MessageBox.Show("Zu Plotgruppe '" + groupP.Name + "' keine FID gefunden!");  
         }  
         DialogResult result1 = MessageBox.Show("Achtung: Job Status richtig gesetzt?\nBatchPlot für Gruppe: '" + groupP.Name + "' (FID=" + FIDPlotGruppe+")?",  
         "BatchPlot starten?",  
         MessageBoxButtons.YesNo);  
         if (result1 != DialogResult.Yes)  
           return;  
         // alle Plots durchgehen  
         foreach (Feature feature in tabPLT_PLOT_GROUP_PLOT.GetFeatures(true))  
         {  
           long FIDPlot = feature.Attributes.Get("FID_PLOT").ValueLong;  
           long FIDPlotGroup = feature.Attributes.Get("FID_PLOT_GROUP").ValueLong;  
           //Gruppe "Test"  
           if (FIDPlotGroup != FIDPlotGruppe)  
             continue;  
           IPlot plot = myLibrary.FindPlot(FIDPlot);  
           PltPlotRenderer r = PltPlotRendererFactory.CreateRenderer(RendererConfiguration.Plot, plot, RenderingCompatibility.CurrentVersion);  
           r.Render();  
           bool plotResult = SimplePlot();  
           // DWG schliessen  
           Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.CloseAndDiscard();  
           //Plot in anderen Ordner verschieben  
           if (FIDPlotGruppeVerschieben != null)  
           {  
             Autodesk.Map.IM.Data.Tools.RecordWriter writer = new Autodesk.Map.IM.Data.Tools.RecordWriter(myConnection, "plt_plot_group_plot");  
             writer.Clear();  
             // Key - wird als WHERE Clause für das Update verwendet (...where user_id = userid)  
             writer.WriteKey("fid_plot", FIDPlot);  
             // neuer Werte  
             long lFID = (long)FIDPlotGruppeVerschieben;  
             writer.Write("fid_plot_group", lFID);  
             // Update ausführen  
             writer.Update();  
           }  
         }  
       }  
       catch (ConnectionInvalidatedException ex)  
       {  
         MessageBox.Show("Plot Connection ungültigt. \n" + ex.Message.ToString());  
       }  
     }  
      public bool SimplePlot()  
     {  
       Document doc =  
        Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;  
       Editor ed = doc.Editor;  
       Database db = doc.Database;  
       Transaction tr =  
        db.TransactionManager.StartTransaction();  
       using (tr)  
       {  
         // We'll be plotting the current layout  
         BlockTableRecord btr =  
          (BlockTableRecord)tr.GetObject(  
           db.CurrentSpaceId,  
           OpenMode.ForRead  
          );  
         Layout lo =  
          (Layout)tr.GetObject(  
           btr.LayoutId,  
           OpenMode.ForRead  
          );  
         // We need a PlotInfo object  
         // linked to the layout  
         PlotInfo pi = new PlotInfo();  
         pi.Layout = btr.LayoutId;  
         // We need a PlotSettings object  
         // based on the layout settings  
         // which we then customize  
         PlotSettings ps =  
          new PlotSettings(lo.ModelType);  
         ps.CopyFrom(lo);  
         // The PlotSettingsValidator helps  
         // create a valid PlotSettings object  
         PlotSettingsValidator psv =  
          PlotSettingsValidator.Current;  
         // We'll plot the extents, centered and  
         // scaled to fit  
         /*  
         psv.SetPlotType(  
          ps,  
          Autodesk.AutoCAD.DatabaseServices.PlotType.Extents  
         );  
         psv.SetUseStandardScale(ps, true);  
         psv.SetStdScaleType(ps, StdScaleType.ScaleToFit);  
         psv.SetPlotCentered(ps, true);  
          * */  
         // We'll use the standard DWF PC3, as  
         // for today we're just plotting to file  
         /*  
         psv.SetPlotConfigurationName(  
          ps,  
          "DWF6 ePlot.pc3",  
          "ANSI_A_(8.50_x_11.00_Inches)"  
         );  
          * */  
         // We need to link the PlotInfo to the  
         // PlotSettings and then validate it  
         pi.OverrideSettings = ps;  
         PlotInfoValidator piv =  
          new PlotInfoValidator();  
         piv.MediaMatchingPolicy =  
          MatchingPolicy.MatchEnabled;  
         piv.Validate(pi);  
         // A PlotEngine does the actual plotting  
         // (can also create one for Preview)  
         if (PlotFactory.ProcessPlotState ==  
           ProcessPlotState.NotPlotting)  
         {  
           PlotEngine pe =  
            PlotFactory.CreatePublishEngine();  
           using (pe)  
           {  
             // Create a Progress Dialog to provide info  
             // and allow thej user to cancel  
             PlotProgressDialog ppd =  
              new PlotProgressDialog(false, 1, true);  
             using (ppd)  
             {  
               ppd.set_PlotMsgString(  
                PlotMessageIndex.DialogTitle,  
                "Custom Plot Progress"  
               );  
               ppd.set_PlotMsgString(  
                PlotMessageIndex.CancelJobButtonMessage,  
                "Cancel Job"  
               );  
               ppd.set_PlotMsgString(  
                PlotMessageIndex.CancelSheetButtonMessage,  
                "Cancel Sheet"  
               );  
               ppd.set_PlotMsgString(  
                PlotMessageIndex.SheetSetProgressCaption,  
                "Sheet Set Progress"  
               );  
               ppd.set_PlotMsgString(  
                PlotMessageIndex.SheetProgressCaption,  
                "Sheet Progress"  
               );  
               ppd.LowerPlotProgressRange = 0;  
               ppd.UpperPlotProgressRange = 100;  
               ppd.PlotProgressPos = 0;  
               // Let's start the plot, at last  
               ppd.OnBeginPlot();  
               ppd.IsVisible = true;  
               pe.BeginPlot(ppd, null);  
               // We'll be plotting a single document  
               pe.BeginDocument(  
                pi,  
                doc.Name,  
                null,  
                1,  
                true, // Let's plot to file  
                "c:\\temp\\"+lo.LayoutName  
               );  
               // Which contains a single sheet  
               ppd.OnBeginSheet();  
               ppd.LowerSheetProgressRange = 0;  
               ppd.UpperSheetProgressRange = 100;  
               ppd.SheetProgressPos = 0;  
               PlotPageInfo ppi = new PlotPageInfo();  
               pe.BeginPage(  
                ppi,  
                pi,  
                true,  
                null  
               );  
               pe.BeginGenerateGraphics(null);  
               pe.EndGenerateGraphics(null);  
               // Finish the sheet  
               pe.EndPage(null);  
               ppd.SheetProgressPos = 100;  
               ppd.OnEndSheet();  
               // Finish the document  
               pe.EndDocument(null);  
               // And finish the plot  
               ppd.PlotProgressPos = 100;  
               ppd.OnEndPlot();  
               pe.EndPlot(null);  
             }  
           }            
           return true;  
         }  
         else  
         {  
           ed.WriteMessage(  
            "\nAnother plot is in progress."  
           );  
           return false;  
         }  
       }  
     }  
   }  
 }  

Friday, 29 January 2016

VBA enabler slows down AutoCAD

Here is one example for the known issue that VBA enabler slows down AutoCAD.

If you insert a block using _insert it will be quick (in our case 3-4 secs) for a larger drawing inserted. When inserted via command line without dialog box (for example: _-insert "C:/temp/ow.dwg") ist takes more then 30sec.

It happens with Map 2013 SP2, 64bit. Apparently this performance issue has been fixed and doesn't occur anymore in newer releases. Shame that it took me quite a long time to figure out, that VBA was the culprit.

Map 2013, SP2

Tuesday, 26 January 2016

FME - Oracle Spatial to SDF

I just discovered an issue when converting Oracle Spatial data to SDF with FME. 

FME translates Oracle's NUMBER(1,0) to DECIMAL(1,0). In Oracle the column contains the following values: 0, 1, <null>. FME translates  value "1" as "-1e+031". One needs to change the data type in SDF file from DECIMAL to INT to avoid this issue.

FME 2014 SP4, 64bit

SDF file - column data type 


...value '1'' translated to -1e+031.



Thursday, 21 January 2016

AutoCAD Map - swiss reference frame LV95

The reference frame in Switzerland is changing. For all Map users in Switzerland this information provided by Autodesk Support might be of interest. It concerns the Map internal coordinate systems library and transformation algorithm between those systems with regards to LV03 / LV95:

"Map 3D Geodätische Transformation von Datum CH1903 nach CH1903+ (LV03 nach LV95) benutzt NTv2 grid File mit gitterbasierter Interpolation. Es wird keine strenge Transformation mit dem FINELTRA Algorithmus angewendet. Die Genauigkeit bei gitterbasierte Interpolation ist reduziert und ist bei entsprechender Anwendung zu berücksichtigen. Ich nehme an das in der amtlichen Vermessung NTv2 nicht genügt und daher zwingend FINELTRA Algorithmus genommen werden muss."


Map 2013, SP2

Monday, 4 January 2016

Convert Map/FDO lines to ACAD lines

There are situations when you have Map /FDO features but want to use an AutoCAD function, such as dimensions. With Map Industry Models you can apply dimensions on IM features but it is less comprehensive than AutoCAD dimensions. So I wrote - on request - a little tool to convert Map /FDO lines to ACAD polylines "on the fly".

Today another use case for this came up. Sometimes lines from one IM feature source (such as road axis) need to be "copied" to a second feature class (such as cycle paths).  There is no "copy" for IM / Map / FDO geometries in Map. Now the workflow is:
- convert Map / FDO lines to polylines in your drawing ("on the fly")
- create new IM feature and use polyline as geometry input

Map 2013, SP2