Monthly Archives: December 2007

这位CSDN的老兄

今天有朋友告诉我,有位CSDN的yujinming老兄将俺前面搞的VisualSVN 1.3.2的patch拿过去当自己的东西去挣分去了。尽管都是闹着玩的,也得打个招呼吧,算是基本的礼貌。

http://download.csdn.net/source/315086

共有评论2条

  rabbit2003 发表评论 [支持:0人 反对:0人]

    很好用,非常感谢! License type: Corporate Licensed to: tong (999 seats) License expires on 2009-12-25
    时间:2007-12-28 09:35:04 来自:58.60.176.*
    支持反对

  wly0902 发表评论 [支持:0人 反对:0人]

    很好用,非常感谢! License type: Corporate Licensed to: tong (999 seats) License expires on 2009-12-25

Talking about 判断空字符串的问题

That’s right. Looking at Length property equals to zero or not will be much faster than string equality comparison. In a more common case, use IsNullOrEmpty method instead in .net 2.0. This method checks the coming argument null or not, and then call String.get_Length to compare with 0.

Quote

判断空字符串的问题

一般人都喜欢用 (a==null || a=="" )来判断字符串是否是空的,也有部分人会用 String.IsNullOrEmpty(a)来判断(这里要大骂一下微软写MSDN帮助的人,这个方法的说明居然明显地写错了,包括IDE中的代码提示也是错的!!也许因此很多人本来知道这个方法的却被这个提示给误导了!)

现在回到假设已经知道a不是null的情况,我们需要判断a是否是空字符串,如何判断呢? a=="" 估计是大部分人的选择,当然这个时候继续用String.IsNullOrEmpty(a)也是无可厚非的,但是毕竟多了一句废代码, 也许大部分人认为把一个字符串和""比较是一个简单的操作,不过事实上并不是这样~~~~~~~

让我们ILDASM一下,看看这个 == 运算符到底做了什么事:

代码首先是把null的情况当特殊情况先处理了,然后

(省略了MSIL equalhelper代码)
是否感觉有些可怕?在a是空字符串的情况下,2个空字符串比较将会执行多少代码啊,首先是先比较字符串的长度,如果长度不同则返回假,可是此时2个字符串都是长度为0所以不符合这个条件,这个分别锁住a和""这两块内存,然后初始化4个指针来指向它们,再用2个复杂的循环和表达式运算,移动指针比较(很庆幸,由于是空字符串,2个循环实际上分别只判断了一次,并不会执行循环体)然后再判断字符串的长度~~~~ 至此,a=="" 返回了一个true ~~~

事实上事情本可以更加简单得多,如果我们使用另外一个表达式 a.Length == 0 则世界将美好许多,不再需要指针,不再需要那些复杂的语句,我们需要的仅仅是:

.method public hidebysig specialname instance int32 get_Length() cil managed internalcall

事实上在上面的L_0001和L_0009的最终语句里这句话已经被调用了2次了~~~

所以下次需要判断一个字符串是不是空字符串的时候,使用a.Length==0要比a=="" 执行起来快得多,需要执行的机器指令也少得多!

ISO 19115 editor

一直在关注ISO 19115元数据有关的一些进展。
它的editor比较有名的包括:
– GeoNetwork OpenSource Desktop
– DISY Preludio
– ISO Metadata Editor (IME)
– CatMDEdit
– M3Cat
– MetaD
– ArcCatalog ISO Wizard
另外,OGC catalogue service也是比较有意思的一个东西。大家感兴趣可以从Google搜索以上产品。

Extending ArcGIS Command: A case tutorial (II)

ArcGIS command扩展:ArcGIS 9.2/C# 2的实现
Zhuotong Nan (南卓铜) (zhn1@pitt.edu)

(上节介绍了如何使用VS 2005 C# express来帮助我们生成ArcGIS Command扩展的代码框架。)

下面我们来介绍框架代码各部分的具体含义。

/// <summary>
/// Command that works in ArcMap/Map/PageLayout
/// </summary>
[Guid("eb9b3424-41d4-44e9-8ab0-c4740339db95")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyArcGISClassLibrary.BatchedChangeDatasource")]
public sealed class BatchedChangeDatasource : BaseCommand

ArcGIS是基于COM构建的,它只支持同样符合COM规范的动态链接库(Class library)。Guid必须不与任何一个COM组件相同。默认会生成一个新的全局标识符。我们也可以使用ArcGIS菜单里Developer Tools下面的Guid Generator来获取新的标识符(图10)。

image
图10,ArcGIS为我们提供了生成Guid的工具

ClassInterface和ProgId仍然是作为将.net类库导出成COM需要的一些属性。seal关键词指定了本命令不能再被继承。需要注意的是此类从BaseCommand上继承过来,BaseCommand实现了ICommand接口。9.2里BaseCommand放到ADF.BaseClasses命名空间下面,在之前版本是放在Utilities.BaseClasses下面。同时一些BaseCommand有关的辅助命令现在也放到了ADF.CATIDs空间。Utilities命名空间仍然存在,但当使用该空间下的BaseCommand在编译时会有deprecated的警告。

COM Registration Function(s)折叠起来的代码用于实现DLL到COM的自动注册和卸载过程。对于一般的应用,使用其生成代码就足够。

public BatchedChangeDatasource()构造函数里初始化了一些Command必要的信息,比如该Command所在的类别,这是以后在使用命令的时候存放的类别(如图11),同时也连接了一个命令表示的图标(m_bitmap)。这些变量是其基类BaseCommand里继承过来的。

string bitmapResourceName = GetType().Name + ".bmp";

如果不使用默认图标,则需要更改这里的名称。

Overriden Class Methods区域是需要开发者具体完成实现的地方。OnCreate在命令附加到ArcGIS时调用,一般这里传入的Object就是具体的Application,这里将Application对象存储到m_hookHelper.Hook属性里。通过Hook属性可以与ArcGIS Application进行必要的互访问。默认的OnCreate功能十分简单,只是判断了Application是否打开,Application是否有活动的View,如果有则使本命令可用。更多的判断则需要开发者来实现。

OnClick()是用户在点击Command后进行的操作。本案例中进行批量更改DataSource等功能便需要在这里实现。

此外,框架代码还将Project Build Properties设置成 Register for COM interop。对于手工生成的项目,必须记得设置此项。

image
图11,Project Build Properties设置

在具体添加代码前,我们先编译一下,看是否有问题。不幸的是默认的框架编译时会提示缺少引用。我们添加引用后(图13)编译通过。

image
图12,缺少引用错误

image 
图13,增加ESRI.ArcGIS.Display引用

在类最前面添加私有变量声明。

public sealed class BatchedChangeDatasource : BaseCommand
{
    private ESRI.ArcGIS.Framework.IApplication _app;
    private string _layerNameToBeReplaced = "nwbi3";
   

_app接管Hook对象保管Application。_layerNameToBeReplaced指定要被替换Data Source的Layer名。同名的Layer会存在于不同的Data Frame里面。

因为我们需要实现的功能只对ArcMap有用,所以需要判断宿主应用程序是否MxApplication,如否,则禁用本命令。将OnCreate里的下面代码:

if (m_hookHelper == null)
        base.m_enabled = false;
else
        base.m_enabled = true;

更改成:

if (m_hookHelper == null)
    base.m_enabled = false;
else
{
    _app = hook as ESRI.ArcGIS.Framework.IApplication;
    if (_app is ESRI.ArcGIS.ArcMapUI.IMxApplication)
        base.m_enabled = true;
}

现在主要的功能是在OnClick里实现。要完成批量替代Data Source的功能,一般的思路是这样:在当前的Document(一个.Mxd文件即Document)里找到各个Data Frame(在ArcObjects,每个Data Frame就是一个Map),迭代各Map。在每个Map里迭代各Layer,判断此Layer是否具备指定的_layerNameToBeReplaced的名称,如是,将该Layer的Data Source代换成指定的。为了实现时间序列的代换,则还需要先形成目标Dataset Name的List,然后逐一代换。以下代码实现了将一个给定Layer Name的Feature层替换成固定的某个存储在File Geodatabase里的FeatureClass。targetGDBpath指定File Geodatabase的路径,具体的FeatureClass由 fcn.Name指定。如果此FeatureClass是存储在Geodatabase里的某个Feature Dataset里,可由fcn.FeatureDatasetName来指定。最终的Data Source的更换由IDataLayer.Connect来实现。

public override void OnClick()
{
    ESRI.ArcGIS.ArcMapUI.IMxDocument pDoc = (ESRI.ArcGIS.ArcMapUI.IMxDocument)_app.Document;
    if (pDoc == null) return;

    //_layerNameToBeReplaced ="nwbi3";
    string targetGDBpath = "c:tempfileGDB.gdb";

    ESRI.ArcGIS.Carto.IMaps maps = pDoc.Maps;
    ESRI.ArcGIS.Carto.IMap map;
    ESRI.ArcGIS.Carto.ILayer layer;
    ESRI.ArcGIS.Carto.IDataLayer dLayer;
    ESRI.ArcGIS.Geodatabase.FeatureClassNameClass fcn = new ESRI.ArcGIS.Geodatabase.FeatureClassNameClass();

    string conTofGDB = @"DATABASE= " + targetGDBpath;

    ESRI.ArcGIS.Geodatabase.IDataset ds = fGDBWorkspaceFromString(conTofGDB) as ESRI.ArcGIS.Geodatabase.IDataset;

    ESRI.ArcGIS.Geodatabase.IWorkspaceName wsName = ds.FullName as ESRI.ArcGIS.Geodatabase.IWorkspaceName;
    fcn.WorkspaceName = wsName;
    //IDatasetName dsName = ds.Subsets.Next().FullName as IDatasetName;
    //fcn.FeatureDatasetName = dsName;

    fcn.Name = "NWBI3";

    for (int i = 0; i < maps.Count; i++)
    {
        //each data frame
        map = maps.get_Item(i);
        for (int j = 0; j < map.LayerCount; j++)
        {
            layer = map.get_Layer(j);
            if (layer.Name == _layerNameToBeReplaced)
            {
                dLayer = layer as ESRI.ArcGIS.Carto.IDataLayer;
                if (dLayer != null)
                {

                    bool rev = dLayer.Connect(fcn);
                }
            }

        }
    }

    pDoc.ActiveView.Refresh();
}

COM风格的编程经常会看到很多的接口,不同的接口间不停的转换。其实接口一般是按功能进行分类,一个类通常实现了多个接口。比如FeatureLayer会实现不同的例如IDataLayer, IFeatureLayer, IDataset等,IDataLayer负责与数据源有关的操作,IFeatureLayer负责跟具备FeatureClass有关的操作,而IDataset实现了FeatureLayer作为Dataset有关的一些操作。

对Interface的熟悉应用建立在对相关类熟知的基础上。.net help十分全面,查找某接口,可以回溯到应用该接口的全面Class,而每个Class也会给出其实现的各个接口。通过这种方法,我们可以在一定程度上知道哪些接口是可以相互转换的。比如,IMap.get_Layer()查找帮助我们知道是返回ILayer,再结合我们期望是找到一个FeatureLayer,所以寻找FeatureLayerClass的实现的接口,发现其与数据源有关的操作封装在IDataLayer和IDataLayer2里。于是我们将layer cast成IDataLayer,再进行Connect操作。

以上代码中,fGDBWorkspaceFromString方法实现如下,作用是通过构建连接字符串来生成IWorkspace。

public ESRI.ArcGIS.Geodatabase.IWorkspace fGDBWorkspaceFromString(String connectionString)
       {
           ESRI.ArcGIS.Geodatabase.IWorkspaceFactory2 workspaceFactory;
           workspaceFactory = (ESRI.ArcGIS.Geodatabase.IWorkspaceFactory2)new ESRI.ArcGIS.DataSourcesGDB.FileGDBWorkspaceFactoryClass();
           return workspaceFactory.OpenFromString(connectionString, 0);
       }

至此,我们完成了一个很简单的可运行的Command扩展。回到构造函数,将m_category赋为“tong’s Extensions”。编译后,打开ArcMap,打开tools菜单下的Customize…,弹出窗口如图14。左边的Categories可以找到tong’s Extensions,右边的Commands可以看到我们的BatchedChangeDatasource。选定该命令,可以拖拉到任何工具栏。Close此Customize窗口后,命令就可以如内置一样运行。

 image
图14,ArcMap的定制窗口

剩余的问题

对前面设定的期望目标,我们可以通过以下方法形成一个时间序列Dataset的名称。

private List<string> TargetRasterNames()
{
    //DateTime start = new DateTime(1997, 1, 27, 0, 0, 0);
    DateTime start = new DateTime(2002, 8, 19, 0, 0, 0);
    //DateTime end = new DateTime(1997, 1, 29, 0, 0, 0);
    DateTime end = new DateTime(2002, 8, 20, 0, 0, 0);
    List<string> rev = new List<string>();
    for (DateTime dt = start; dt < end; dt = dt.AddHours(1) )
    {
        //string dtStr = string.Format("c{0:MMddHH}c_3hm", dt);
        string dtStr = string.Format("{0:yyyyMMddHH}.asc", dt);
        rev.Add(dtStr);
    }
    return rev;

}

在Connect的时候,根据次序可以赋给不同的RasterName。在更换数据源时,如果确保都在同一Workspace下,则可以通过下面代码简单代换。

if (layer.Name==_layerNameToBeReplaced)
{
          dLayer= layer as IDataLayer;
          if (dLayer!=null )
          {
                 IRasterDatasetName pName = dLayer.DataSourceName as IRasterDatasetName;
                 IDatasetName dsName = pName as IDatasetName;
                 ii++;
                 dsName.Name = targets[ii];
            }
}

如果不在同一Workspace,IName还需要指定IWorkspaceName。

结论

本文结合实际工作的一个案例,演示了如何应用Visual Studio 2005 C# Express来开发扩展ArcGIS Command。以上演示还有很多需要改进的地方,比如要代换的Layer名,不能内置在代码里,我们可以通过生成一个Dialog让使用者来决定替换哪一层,再比如,我们可以设置更漂亮的图标以区别于已有的图标。但这些都已经不是本tutorial的内容。本tutorial的目的是希望大家通过阅读本文可以初步掌握如何使用C#进行ArcGIS扩展,大家更多需要关注的是OnClick里的Business logic。

有什么问题有与我联系或留言。转载请保留帖子全部信息。

Extending ArcGIS command: a case tutorial

ArcGIS command扩展:ArcGIS 9.2/C# 2的实现
Zhuotong Nan (南卓铜) (zhn1@pitt.edu)

为什么要做扩展?

很简单,ArcGIS提供了通用的功能,但不可能提供每个大家需要的功能。而且,对一些专业(如水文)也许最实用的功能都没有。这时候便需要对现有的ArcGIS进行扩展。

如何扩展?

有很多方法了,比如AML来扩展ArcInfo Workstation——我以前写过一个AML的简要指南,Google应该能找到。在Desktop下,可以用VBA扩展,或者用任何支持COM的现代语言来扩展。这里用Visual Studio C# 2005 Express来开发。对于独立应用程序,应当购买ArcEngine。ArcEngine可以认为是ArcObjects的一个子集。

准备工作

需要ArcGIS desktop .net developer kit。如果最初安装ArcGIS 9.2的时候没有安装上,那么到安装/删除程序里去重新修改安装。注意事先机器上得已经存在.net framework SDK,如果没有,安装ArcGIS看不到.net developer kit选项。.net SDK不同于平时大家安装的redistribution .net framework runtime。请到微软官方网站下载。

安装Visual Studio C# 2005 Express,建议在安装ArcGIS desktop .net developer kit之前进行,ArcGIS desktop .net developer kit将在Visual Studio C# 2005 Express上生成一些项目模板(如ArcMap class library),应用这些模板进行开发会节省很多时间。

我们的工作

如图1所示,要在一个Document里生成多个Map(多个Data Framework),Map里的内容是时间序列的小时降水量(不同的Raster Dataset)。右图仅演示了1天24小时的图。我们的挑战是要做10天,而且必须各个图采用同样的图例。

 
图1, 小时降水量图,我们希望实现的目标

解决方法

先设计好一个Data Frame,设置好Legend(不能是sketch,否则系列图就没有意义)。然后复制24份Data Frame,应用ArcMap的页面排版功能进行排版。然后将此文档复制10份(10天)。现在的问题是如何去更改每个Data Frame里的Data Source。我们需要的是实现一个批量处理的方法。采用ArcObjects进行二次开发,生成一个Command来完成这个事情,是个不错的方案。

建立一个新项目,点击File>New Project…(图2)。因为我们只想进行基于ArcMap的扩展,这里选择ArcMap Class Library(图3),敲入项目名称“MyArcGISClassLibrary”(不包括引号)。一般而言,项目按功能进行归类,一个可以实现很多的Command或者Tools扩展。所以没有必要对每个Command或者Tool都建项目。

image
图2,新建项目

 image
图3,新建ArcMap Class Library

在随后出现的ArcGIS Project Wizard(图4),添加必要的引用。这里选择ESRI.ArcGIS.ArcMap,ESRI.ArcGIS.ArcMapUI,ESRI.ArcGIS.Carto,ESRI.ArcGIS.DataSourceGDB,ESRI.ArcGIS.DataSourceRaster,ESRI.ArcGIS.Framework,ESRI.ArcGIS.Geodatabase,ESRI.ArcGIS.System。这些都是常用的库,添加的这些库,会出现在VS项目的Reference里。如果这里不添加,也可以以后根据需要再行添加(如图4.1)。需要注意的是,这里用的ESRI.ArcGIS.DataSourceGDB是9.2新出来的File Geodatabase,据称比Personal Geodatabase性能要好,而且有更大的存储许可。点击Finish结束。

image 
图4,ArcGIS Project Wizard

image 
图4.1 可以从Solution的这个位置添加引用

该向导帮助我们添加了必要的Reference,并新建了Class1.cs。但我们不需要它。如图5所示,删除。

 image
图5,删除Class1.cs

移动Solution Explorer窗口的项目上,如图6,添加一个新项,点击New Item…。在新弹出的窗口中选择Base Command(图7)。并设置名称为"BatchedChangeDatasource.cs"。在New Item Wizard Options里选择ArcMap Command(如图8)。

image 
图6,新建Command类

image 
图7,选择Base Command

image 
图8,选择ArcMap Command

点击OK,Wizard生成足够多的东西(如图9),我们在此基础上再写一些代码以完成我们的预期功能。

image 
图9,Wizard生成的代码框架按Ctrl+S保存此Solution。Solution名称设成MyArcGISClassLibrary.sln。

(下一节我们将介绍代码的作用,和如何添加自己的功能以实现既定目标。)

sql server 2005 express file database readonly

当开发环境里的sql server 2005 express数据库文件copy到发行环境里,往往面临权限更改的问题。
比如开发环境里是 nt authority/xxx windows集成帐号,但发行环境里不可能保证这个帐号和密码与开发环境一致。一个较好的方法是将之设置为 sql server帐号,比如使用sa和相关的密码(更安全自然是使用专门的帐号,而不用sa)
但即便如此,copy到发行环境里,还会出现数据库文件只读的问题。这一般是由于在sql server 05 express使用的network service权限不足够更改数据库文件。一个方便的解决方法是将数据库文件所在的目录权限增加network service,使之安全控制(可能修改和写入也足够)。重启sql server(配置管理器里)。到这一步按我的实验仍是只读。打开management studio express,先将该只读的数据库文件detach掉(好像会提示某个信息说detach失败,没关系),关闭对象链接器然后再重新打开,这时表明已经成功detach掉了,重新attach进来,发现已经可用。
权限的问题有时候整起来十分麻烦,这问题足足花了我三、四个小时。

这个鬼网络,所里远程登录基本不能用

本想更新一下Asian G-WADI,远程登陆到westdc服务器,足足折腾了1个半小时,慢的跟比蜗牛还慢,一个简单的操作要半小时那边才反应过来。终于彻底失去耐性了。这事情以后甭找我干了。
访问美国这儿的网站都能很快反应过来,就国内的网站不知道怎么回事,也都号称是挺快的骨干网上,就是死活慢的离谱的。
刚才试着westdc服务器上从download.microsoft.com下载一个40来m的东西,发现速度在1KB不到,估计所网又在闹病毒全堵塞了,彻底晕倒。