最近公司想在一個產品上加一個電子地圖的功能,讓我有機會接觸到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_data\countries.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_data\rivers.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_data\cities.shp"), true); //Define a blue 1px wide pen //layCities.Style.Symbol = new Bitmap(HttpContext.Current.Server.MapPath (@"~\App_data\icon.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_data\countries.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,我只是對其中一些代碼進行了說明,有需要的 可以去下載。
下次我們將自己做一些更好玩的東西,當我們點擊地圖上某個對象,顯示出這個對象的若干信息,怎 麼樣,酷吧?