大概是2012年的3月,我着手开发AdobeIllustrator CS4下的地图插件,一边了解Adobe Illustrator的架构体系,一边开发地图插件。
Adobe Illustrator的插件,在Windows系统下是采用的传统的动态链接库的形式,只不过是以aip作为后缀名。框架程序应该是用LoadLibrary函数将插件加入到程序模块中,至于插件的加载顺序需要参考Adobe Illustrator的开发帮助文档,可以选择参考我翻译的版本http://download.csdn.net/detail/yiyi31130108/5233003。
Adobe Illustrator提供的开发框架中的dll导出函数如下,它是插件与框架交互的通道。
extern "C" ASAPIASErr PluginMain(char* caller, char* selector, void* message);
采用的消息分流器,caller和selector提供的消息分流的依据,message中提供了消息的详细数据。框架将各种消息传递给插件,插件根据需要进行处理。
知道了这些可以开始地图插件的开发了。我的开发目标是:在Illustrator中提供一个地图编辑的良好支持。平面设计软件,优势在于用户群广,图形展示优雅,用户交互体验好,但缺乏一些专业工具。GIS软件应该来说在图形展示和用户体验上还是不能和平面设计软件相比的,好长时间没有使用过GIS数据处理软件了,不知道现在界面还是不是那么丑,支持地图编辑的效果好不好。总结的来说开发任务很简单,将GIS数据转入Adobe Illustrator制作成地图文档即可,并提供专业工具。
在最开始的开发中,我选择了自己的先前的小型的GIS内核,主要包括文件GIS数据、图形、属性、序列化、格式化,并写了shp的转换代码。后来我将GDAL的OGR部分的代码维护进入地图插件工程取代了原有的GIS模块。
转入中,包括地图信息存取、地图图层信息的存取、要素的存取。其中要素存储包括创建art和存储属性,如构建线状art如下(注意坐标先前根据地图比例尺信息已经转为图面坐标了):
AIArtHandle MdRender::DrawLineString(int nCount, AIReal *pData, boolbClosed)
{
AIArtHandle art = nil;
sAIArt->NewArt(kPathArt,kPlaceAboveAll, nil, &art);
sAIPath->SetPathSegmentCount(art,(short)nCount);
AIPathSegment *segment= new AIPathSegment[nCount];
for(int i = 0; i <nCount; i++)
{
segment[i].p.h= pData[i * 2];
segment[i].p.v= pData[i * 2 + 1];
segment[i].in= segment[i].out = segment[i].p;
segment[i].corner= true;
}
sAIPath->SetPathSegments(art,0, nCount, segment);
delete[] segment;
if(bClosed)
{
sAIPath->SetPathClosed(art,true);
}
return art;
}
下面是存储对应的属性:
ASErr MdTransition::SaveFeature(AIArtHandle art, OGRFeature*pFeature)
{
OGRGeometry*pGeometryRef = pFeature->GetGeometryRef();
int nWkbSize =pGeometryRef->WkbSize();
byte *pWkb = NULL;
if(nWkbSize > 0)
{
pWkb = newbyte[nWkbSize];
pGeometryRef->exportToWkb(wkbNDR,pWkb); //使用小字节序
}
BinaryArchive archive;
fSerialize.Serialize(pFeature,archive);
AIDictionaryRef dic =NULL;
sAIArt->GetDictionary(art,&dic);
sAIDictionary->SetBinaryEntry(dic,fArtGeometrykey, (void*)pWkb, (ASInt32)nWkbSize);
sAIDictionary->SetBinaryEntry(dic,fArtAttributeKey, archive.GetData(), archive.GetSize());
sAIDictionary->Release(dic);
return kNoErr;
}
所有数据都进行文档后。我开发了一个简易的查询功能,但是在GDAL加入后,我弃用了查询。查询使用ADM并提供解析类似sql语句功能,还是具有实际意义,这里还是分享一下开发方法。
ADM是Adobe提供的一套跨平台的用户界面,使用ADM可以在Illustrator中开发出专业的图形界面。我的查询面板继承了Illustrator SDK提供的BaseADMDialog,提供了一个对话框样式的面板。下面的代码可以大概给出一个查询面板构建与使用的主要逻辑:
QueryDialog::QueryDialog(SPPluginRef pluginRef)
:BaseADMDialog()
{
BaseADMDialog::Create(pluginRef, "NFMDQueryDialog",
IDD_DLG_QUE,kADMTabbedFloatingDialogStyle, 0 );
}
ASErr QueryDialog::Init()
{
ASErr error =BaseADMDialog::Init();
ADMDialogRef dialog =this->GetDialogRef();
ADMItemRef item = nil;
item =sADMDialog->GetItem( dialog, IDC_EDIT_QUE );
sADMItem->Enable(item, false );
sADMItem->SetTrackProc(item, QueryTrackProc );
sADMItem->SetMask(item, kADMKeyStrokeMask ); //Track all keystroke events
return error;
}
ADMBoolean QueryDialog::QueryTrackProc(ADMItemRef inItem,ADMTrackerRef inTracker)
{
ADMBooleancommandControlDown, notify = false;
//checks if the(mac)COMMAND (win)CONTROL modifier is pressed
commandControlDown =sADMTracker->TestModifier( inTracker, kADMMenuKeyDownModifier );
//ctrl + enter时执行查询
if(('\n' ==sADMTracker->GetCharacter(inTracker)) && commandControlDown)
{
long length= sADMItem->GetTextLengthW(inItem);
if(length> 0)
{
ADMUnicode*txt = new ADMUnicode[length + 1];
memset(txt,0, 2 * (length + 1));
sADMItem->GetTextW(inItem, txt, length );
gPlugin->fQueryDialog->SearchArt(ai::UnicodeString(txt));
delete[]txt;
}
sADMItem->SetText(inItem,"");
sADMTracker->Abort(inTracker);
notify =true;
}
else
{
notify =sADMItem->DefaultTrack( inItem, inTracker );
}
return notify;
}
查询部分我没有比较好的方案选择,于是傻傻的自己写了一个解析类Sql查询的模块。我的Sql定义为:条件 in图层,其中条件可以由多个一般判断表达式通过and or进行连接。如NAME=”China” in世界。解析时,使用的顺序是:
1. 预处理首尾空格裁剪,中间空格合并
2. 分离图层
3. 分析条件语句分析引号》分析圆括号》分析连接符》分析操作符》转换查询字符串链表到查询链接
查询链接即可用于图层内art遍历时筛选art了。