最近公司想在一个产品上加一个电子地图的功能,让我有机会接触到GIS领域。这几年国内的GIS技术也发展的很快,但是相关的技术文档还是很缺乏,都是英文的。我的文笔不好,写的东西也很浅显,但是希望能给看文章的一点点帮助,我就心满意足了。
我用的 SharpMap是一个开源的Gis项目,功能还可以,最大的特点就是简单易用,适合刚刚接触GIS技术的朋友。同时还使用了MapWindow GIS来帮助画地图,使用NetTopologySuite来完善一些sharpmap没有提供或者功能不全的地方。MapWindow GIS使用起来不是很方便,熟悉了就容易了。关于软件的使用我就不再赘述,大家有问题可以跟帖,我会及时回复。
这次我们根据demo先了解一下如何show一个地图。这是最基本的步骤,也比较简单,希望能对刚入门的同学有所帮助。
我们使用SharpMap.UI.dll中的ajax控件
<smap:AjaxMapControl width="1600px" Height="600px" id="ajaxMap" runat="server"
OnClickEvent="MapClicked" onmouseout="toolTip();"; OnViewChange="ViewChanged" CssClass="Ly" UseCache="false"
OnViewChanging="ViewChanging" ZoomSpeed="1" />
来展示地图,主要代码分析如下:
首先,初始化地图
Initialize Map
public static SharpMap.Map InitializeMap(System.Drawing.Size size)
{
HttpContext.Current.Trace.Write("Initializing map");
//Initialize a new map of size 'imagesize'
SharpMap.Map map = new SharpMap.Map(size);
//Set up the countries layer
SharpMap.Layers.VectorLayer layCountries = new SharpMap.Layers.VectorLayer("Countries");
//Set the datasource to a shapefile in the App_data folder
layCountries.DataSource = new SharpMap.Data.Providers.ShapeFile(HttpContext.Current.Server.MapPath(@"~App_datacountries.shp"), true);
//Set fill-style to green
layCountries.Style.Fill = new SolidBrush(Color.Green);
//Set the polygons to have a black outline
layCountries.Style.Outline = System.Drawing.Pens.Black;
layCountries.Style.EnableOutline = true;
layCountries.SRID = 4326;
//Set up a river layer
SharpMap.Layers.VectorLayer layRivers = new SharpMap.Layers.VectorLayer("Rivers");
//Set the datasource to a shapefile in the App_data folder
layRivers.DataSource = new SharpMap.Data.Providers.ShapeFile(HttpContext.Current.Server.MapPath(@"~App_datarivers.shp"), true);
//Define a blue 1px wide pen
layRivers.Style.Line = new Pen(Color.Blue, 1);
layRivers.SRID = 4326;
//Set up a river layer
SharpMap.Layers.VectorLayer layCities = new SharpMap.Layers.VectorLayer("Cities");
//Set the datasource to a shapefile in the App_data folder
layCities.DataSource = new SharpMap.Data.Providers.ShapeFile(HttpContext.Current.Server.MapPath(@"~App_datacities.shp"), true);
//Define a blue 1px wide pen
//layCities.Style.Symbol = new Bitmap(HttpContext.Current.Server.MapPath(@"~App_dataicon.png"));
layCities.Style.SymbolScale = 0.8f;
layCities.MaxVisible = 40;
layCities.SRID = 4326;
//Set up a country label layer
SharpMap.Layers.LabelLayer layLabel = new SharpMap.Layers.LabelLayer("Country labels");
layLabel.DataSource = layCountries.DataSource;
layLabel.Enabled = true;
layLabel.LabelColumn = "Name";
layLabel.Style = new SharpMap.Styles.LabelStyle();
layLabel.Style.ForeColor = Color.White;
layLabel.Style.Font = new Font(FontFamily.GenericSerif, 12);
layLabel.Style.BackColor = new System.Drawing.SolidBrush(Color.FromArgb(128, 255, 0, 0));
layLabel.MaxVisible = 90;
layLabel.MinVisible = 30;
layLabel.Style.HorizontalAlignment = SharpMap.Styles.LabelStyle.HorizontalAlignmentEnum.Center;
layLabel.SRID = 4326;
layLabel.MultipartGeometryBehaviour = SharpMap.Layers.LabelLayer.MultipartGeometryBehaviourEnum.Largest;
//Set up a city label layer
SharpMap.Layers.LabelLayer layCityLabel = new SharpMap.Layers.LabelLayer("City labels");
layCityLabel.DataSource = layCities.DataSource;
layCityLabel.Enabled = true;
layCityLabel.LabelColumn = "Name";
layCityLabel.Style = new SharpMap.Styles.LabelStyle();
layCityLabel.Style.ForeColor = Color.Black;
layCityLabel.Style.Font = new Font(FontFamily.GenericSerif, 11);
layCityLabel.MaxVisible = layLabel.MinVisible;
layCityLabel.Style.HorizontalAlignment = SharpMap.Styles.LabelStyle.HorizontalAlignmentEnum.Left;
layCityLabel.Style.VerticalAlignment = SharpMap.Styles.LabelStyle.VerticalAlignmentEnum.Bottom;
layCityLabel.Style.Offset = new PointF(3, 3);
layCityLabel.Style.Halo = new Pen(Color.Yellow, 2);
layCityLabel.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
layCityLabel.SmoothingMode = SmoothingMode.AntiAlias;
layCityLabel.SRID = 4326;
layCityLabel.LabelFilter = SharpMap.Rendering.LabelCollisionDetection.ThoroughCollisionDetection;
layCityLabel.Style.CollisionDetection = true;
//Add the layers to the map object.
//The order we add them in are the order they are drawn, so we add the rivers last to put them on top
map.Layers.Add(layCountries);
map.Layers.Add(layRivers);
map.Layers.Add(layCities);
map.Layers.Add(layLabel);
map.Layers.Add(layCityLabel);
//limit the zoom to 360 degrees width
map.MaximumZoom = 360;
map.BackColor = Color.LightBlue;
map.Zoom = 360;
map.Center = new SharpMap.Geometries.Point(0, 0);
HttpContext.Current.Trace.Write("Map initialized");
return map;
}
在这段代码中,
SharpMap.Layers.VectorLayer layCountries = new SharpMap.Layers.VectorLayer("Countries");
layCountries.DataSource = new SharpMap.Data.Providers.ShapeFile(HttpContext.Current.Server.MapPath(@"~App_datacountries.shp"), true);
这两句话定义并初始化了一个map的一个层。一个map可能包含多个层,我们对map进行操作,其实就是对map的各个层进行操作,比如说道路层,建筑层等等。根据业务的复杂程度,可能有很多个层。一般来说,每个层都有各自的抽象特征(一类对象),可以进行一样的操作。各个层组合起来,就是一个map.
好,接着说初始化。这里为VectorLayer的构造函数提供了一个层的名称,然受设置了数据源。一个层的Datasource是一个IProvider类型的数据,而ShapeFile类正是实现了Iprovider接口的类。(这点很重要,以后有很多机会需要把一个datasource转换成ShapeFile)。
接下来是对这个层进行着色,呵呵 就是这个层上的对象如何填充颜色。这里因为是刚刚开始,我们就用最简单的办法,设置了一个
layCountries.Style.Fill = new SolidBrush(Color.Green);
这样,就把层上的对象都填充成了绿色。在以后的讲解中,我们会让大家了解到如何自定义theme,这样我们能根据实现设置的对象的类型填充不同的颜色,以实现自定义的样式,让地图多彩多姿。
然后我们看到label层。每一个map的layer都有自己的label层。
SharpMap.Layers.LabelLayer layLabel = new SharpMap.Layers.LabelLayer("Country labels");
layLabel.DataSource = layCountries.DataSource;
layLabel.Enabled = true;
layLabel.LabelColumn = "Name";
这里我们首先定义了一个label层,然后把countries层的datasource直接赋给label层的datasource就OK了。这样,这个新定义的label层就属于countries层了。然后我们给它制定一个labelColumn,这个是必须指定的。它表示countries层的每个对象应该显示什么内容。这个内容在ShapeFile(我们在MapWindow GIS中建立的shapefile文件)的attribute中进行增加和更新。
我想有必要说一下地图的对象。
地图上有很多点,线和多边形。这些都是对象。我们都可以对他们进行操作。每个对象有相应的属性,这些属性我们也可以添加,修改和删除。而上述的label的LabelColumn就是这些属性中的一个。我们在这里可以指定一个,也可以通过LabelStringDelegate来委托一个方法返回一个字符串(这个将在以后进行详细讲解)。
OK,在设置了label的字体颜色,大小和背景色之后,有两个个属性是特别值得我们注意的。
layLabel.MaxVisible = 90;
ayLabel.MinVisible = 30;
这两个值说明了我们在多大/多小的比例尺下可以看到这个label层。我就经常会忽略这个问题,以至于在放大或缩小比例尺的时候看不到label,也找不到原因。
在初始化的最后,我们把所有的层都加到地图中来,在设置一下最大的比例尺,背景色和中心点,就可以了
map.Layers.Add(layCountries);
map.Layers.Add(layRivers);
map.Layers.Add(layCities);
map.Layers.Add(layLabel);
map.Layers.Add(layCityLabel);
//limit the zoom to 360 degrees width
map.MaximumZoom = 360;
map.BackColor = Color.LightBlue;
最后,我们通过一个maphandler(ashx)把地图输出到页面上。
System.Drawing.Bitmap img = (System.Drawing.Bitmap)map.GetMap();
context.Response.ContentType = "image/png";
System.IO.MemoryStream MS = new System.IO.MemoryStream();
img.Save(MS, System.Drawing.Imaging.ImageFormat.Png);
// tidy up
img.Dispose();
byte[] buffer = MS.ToArray();
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
这样,简单的地图就出来了。怎么样,有成就感吗?呵呵
这里的代码都来自www.codeplex.com/sharpmap的demo,我只是对其中一些代码进行了说明,有需要的可以去下载。
下次我们将自己做一些更好玩的东西,当我们点击地图上某个对象,显示出这个对象的若干信息,怎么样,酷吧?