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