DotSpatial
A DotSpatial egy nyílt forráskódú térinformatikai programkönyvtár, amely különböző funkcionalitásokat biztosít téradat kezelés, betöltés és mentés, valamint elemzési feladatokra. A programkönyvtár C#-ban íródott és a .NET 4.0 keretrendszeren alapszik. Elsősorban asztali alkalmazások elkészítését segíti elő, amely a Windows Forms platformra épülnek. A programcsomag GNU LGPL licenc alatt használható.
Tartalomjegyzék
Története
A könyvtár létrehozását az Idaho állami egyetemen (Idaho State University) fejlesztett MapWindow alkalmazás ihlette. Kezdetben csak a saját belső fejlesztésüket szerették volna megkönnyíteni különféle központi funkciók kiszervezésével, idővel azonban a felhalmozott szolgáltatások száma elérte azt a méretet, hogy a fejlesztők úgy érezték, érdemes kiadni a nagyközönség számára is. Sajnos a folyamatosan rétegződő, és központi koncepció nélküli fejlesztés rányomta a bélyegét a kódbázisra. Ennek megfelelően modulonként, vagy akár azokon belül is erősen eltérő megközelítéseket láthatunk a forráskódban.
Szolgáltatások
- Térinformatikai adatok megjelenítése .Net Windows Form, vagy Web alkalmazásokban
- Shapefájlok, rácshálók valamint raszter felvételek olvasása.
- Menet közbeni projektálás
- Tudományos analízis
- GPS adatok olvasása
Kik használják?
Használat
Telepítést nem igényel, ha használni szeretnénk, akkor két lehetőségünk közül választhatunk.
- Letölthetjük azt össze modult egyben a http://dotspatial.codeplex.com/ oldalról, majd később a szükséges projektekbe referenciázzuk őket.
- Visual Studio 2010-től felfelé használhatjuk a NuGet csomagkezelő, ahonnan telepíthetjük az egyes modulokat a kiválasztott projektek alá. Nagy előnye, hogy csomag frissülése esetén automatikusan eljut hozzánk is az új csomag.
Windows Forms vezérlők
A DotSpatialt egyik legnagyobb előnye az előre megírt kontrolok, amiket a már jól megszokott Windows Formsos recept alapján akár vizuális szerkesztőben is összeállíthatunk.
DotSpatialt vezérlők telepítése
Ha szeretnénk használni a grafikus szerkesztőt, akkor mindenekelőtt hozzá kell adnunk a Toolbox-hoz a DotSpatialt vezérlőit.
- Jobbklikk a Toolboxon, majd válasszuk ki az Add Tab menüpontot.
- Nevezzük az előbb hozzáadott fület.
- Ismét jobbklikk az új fülön, és válasszuk ki a Choose Item opciót.
- A felugró ablakból válasszuk ki a DotSpatial.Controls.dll-t, ha nem találjuk a listából, akkor manuálisan adjuk hozz.
Most már ugyanúgy használhatjuk DotSpatial vezérlőit, mit a .NET alapértelmezettjeit.
Vezérlők demonstrációja
A DotSpatial vezérlők segítségével rendkívül gyorsan tudjuk alkalmazásunkat felruházni térinformatikai képességekkel. A következő alkalmazás remek példa az előbbiekre. Először készítsük el a felületet az alapértelmezett .NET, illetve a már ismertetett DotSpatial vezérlőkből.
<KÉP>
Miután megfelelően elneveztük a felületre elhelyezett elemeket, a hozzájuk tartozó kód mindössze ennyi:
1 public partial class DesktopMapping : Form
2 {
3 private AppManager _AppManager;
4
5 public DesktopMapping()
6 {
7 InitializeComponent();
8
9 _AppManager = new AppManager();
10 _AppManager.LoadExtensions();
11 }
12
13 private void uxOpenFile_Click(object sender, EventArgs e)
14 {
15 uxMap.AddLayer();
16 }
17
18 private void uxZoomIn_Click(object sender, EventArgs e)
19 {
20 uxMap.ZoomIn();
21 }
22
23 private void uxZoomWide_Click(object sender, EventArgs e)
24 {
25 uxMap.ZoomToMaxExtent();
26 }
27
28 private void uxPan_Click(object sender, EventArgs e)
29 {
30 uxMap.FunctionMode = DotSpatial.Controls.FunctionMode.Pan;
31 }
32
33 private void uxGenShp_Click(object sender, EventArgs e)
34 {
35 var rnd = new Random();
36 var pg = new Polygon[100];
37 var f = new Feature();
38 var fs = new FeatureSet(f.FeatureType);
39
40 for (int i = 0; i < 100; i++)
41 {
42 var center = new Coordinate((rnd.Next(50) * 360) - 180, (rnd.Next(60) * 180) - 90);
43 var coord = new Coordinate[50];
44 for (int ii = 0; ii < 50; ii++)
45 {
46 coord[ii] = new Coordinate(center.X + Math.Cos((ii * 10) * Math.PI / 10), center.Y + (ii * 10) * Math.PI / 10);
47 }
48 coord[35] = new Coordinate(coord[0].X, coord[0].Y);
49 pg[i] = new Polygon(coord);
50 fs.Features.Add(pg[i]);
51 }
52 fs.SaveAs("C:\\Temp\\test.shp", true);
53 }
54 }
Bővítmények
Lehetőségünk van bővítmények készítésére, amelyek akár a már kész alkalmazásokba is könnyedén integrálódnak (ilyen például a MapWindow).
Példaprogram beépülő modulra
A bemutatásra kerülő bővítménnyel lehetőségünk lesz vonalakat rajzolni, réteget létrehozni, illetve megjeleníthetünk velük kapcsolatos magassági adatokat. Elsőnek le kell töltenünk az online megtalálható sémával.
Diagram készítése
A magassági adatok megjelenítéséhez egy Windows Form alkalmazást készítünk.
1 public void Plot(double[] data)
2 {
3 chart1.Series.Clear();
4 var series = chart1.Series.Add("Elevation (meters)");
5 series.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
6 series.Points.DataBindY(data);
7 }
Raszter adatok kinyerése
A DotSpatial segítségével könnyedén kinyerhetjük a szükséges magassági adatokat.
1 private static double GetElevation(IMapRasterLayer raster, Coordinate coordinate)
2 {
3 RcIndex rowColumn = raster.DataSet.Bounds.ProjToCell(coordinate);
4 double elevation = raster.DataSet.Value[rowColumn.Row, rowColumn.Column];
5 return elevation;
6 }
7 private static double GetDistance(double x1, double y1, double x2, double y2)
8 {
9 return Math.Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
10 }
Koordináták kinyerése
1 private List<Coordinate> GetCoordinatesFromLine(IMapLineLayer lineLayer)
2 {
3 IFeatureSet featureSet = lineLayer.DataSet;
4
5 // The coordinates should be the first feature of the feature set.
6 IList<Coordinate> lineCoordinates = featureSet.Features[0].Coordinates;
7
8 // Though the original line may only have a few points, we split
9 // each line segment into many points
10 List<Coordinate> pathCoordinates = new List<Coordinate>();
11
12 for (int i = 0; i < lineCoordinates.Count - 1; i++)
13 {
14 Coordinate startCoord = lineCoordinates[i];
15 Coordinate endCoord = lineCoordinates[i + 1];
16 List<Coordinate> segmentCoordinates = SplitSegment(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y);
17
18 //add list of points from this line segment to the complete list
19 pathCoordinates.AddRange(segmentCoordinates);
20 }
21 return pathCoordinates;
22 }
23
24 private static List<Coordinate> SplitSegment(double startX, double startY, double endX, double endY)
25 {
26 const int MinimumDistanceBetweenPoints = 15;
27
28 double points = Math.Floor(GetDistance(startX, startY, endX, endY) / MinimumDistanceBetweenPoints);
29 int PointsPerSegment = (int)Math.Max(points, 1);
30
31 double curX = startX;
32 double curY = startY;
33 double constXdif = ((endX - startX) / PointsPerSegment);
34 double constYdif = ((endY - startY) / PointsPerSegment);
35
36 List<Coordinate> pathPointList = new List<Coordinate>(PointsPerSegment);
37 for (int i = 0; i <= PointsPerSegment; i++)
38 {
39 if (i == 0)
40 {
41 curX = startX;
42 curY = startY;
43 }
44 else
45 {
46 curX = curX + constXdif;
47 curY = curY + constYdif;
48 }
49 Coordinate coordinate = new Coordinate(curX, curY);
50 pathPointList.Add(coordinate);
51 }
52 return pathPointList;
53 }
54
55 private void ShowElevation()
56 {
57 if (!map.GetRasterLayers().Any())
58 {
59 MessageBox.Show("Please add a DEM raster layer to the map.");
60 return;
61 }
62
63 if (!map.GetLineLayers().Any())
64 {
65 MessageBox.Show("Please create a path by left clicking to add points and right-clicking to complete the path.");
66 return;
67 }
68
69 try
70 {
71 IMapRasterLayer rasterLayer = map.GetRasterLayers().First();
72 IMapLineLayer pathLayer = map.GetLineLayers().First();
73 var coords = GetCoordinatesFromLine(pathLayer);
74
75 double[] elevation = new double[coords.Count];
76 for (int i = 0; i < coords.Count; i++)
77 {
78 elevation[i] = GetElevation(rasterLayer, coords[i]);
79 }
80
81 ChartForm chart = new ChartForm();
82 chart.Plot(elevation);
83 chart.Show();
84 }
85 catch (Exception ex)
86 {
87 MessageBox.Show("Error calculating elevation. The whole path should be inside the DEM area. " + ex.Message);
88 }
89 }
SimpleActionItem használata (menüelem)
1 public void ButtonClick(object sender, EventArgs e)
2 {
3 // We're expecting this extension to only be run in a Windows Forms application.
4 // We'll depend on a few Windows Forms (Map) features like MouseDown, so we cast
5 // the App.Map as a Map and store a reference to it.
6 map = App.Map as Map;
7
8 // remove any existing path if needed.
9 if (_PathLineLayer != null)
10 map.Layers.Remove(_PathLineLayer);
11
12 _PathLineLayer = null;
13 _LineFeature = null;
14
15 // Let the user know we are ready for them to set points by changing the cursor.
16 map.Cursor = Cursors.Cross;
17 map.MouseDown += map_MouseDown;
18 }
Vektor réteg létrehozása
1 private void map_MouseDown(object sender, MouseEventArgs e)
2 {
3 if (e.Button == MouseButtons.Left)
4 {
5 // Encourage the user to select a raster, if they haven't done so.
6 if (!map.GetRasterLayers().Any())
7 {
8 map.AddRasterLayer();
9 map.ZoomToMaxExtent();
10 return;
11 }
12
13 StartOrContinueDrawingPath(e.Location);
14 App.ProgressHandler.Progress(null, 0, "Point registered. Click again to add line segment. Right-click to finish.");
15 }
16 else if (e.Button == MouseButtons.Right)
17 {
18 EndDrawingPath();
19 ShowElevation();
20 App.ProgressHandler.Progress(null, 0, "Ready.");
21 }
22 }
23
24 private IFeature AddLineFeatureSetToMap()
25 {
26 FeatureSet lineFeatureSet = new FeatureSet(FeatureType.Line);
27 lineFeatureSet.Projection = map.Projection;
28
29 // Initialize the featureSet attribute table by creating columns
30 DataColumn column = new DataColumn("ID", typeof(short));
31 lineFeatureSet.DataTable.Columns.Add(column);
32 DataColumn column2 = new DataColumn("Number of Points", typeof(int));
33 lineFeatureSet.DataTable.Columns.Add(column2);
34 DataColumn column3 = new DataColumn("Description");
35 lineFeatureSet.DataTable.Columns.Add(column3);
36
37 // Add the featureSet as map layer
38 _PathLineLayer = (MapLineLayer)map.Layers.Add(lineFeatureSet);
39 _PathLineLayer.Symbolizer = new LineSymbolizer(Color.Blue, 2);
40 _PathLineLayer.LegendText = "Path Layer";
41
42 var newList = new List<Coordinate>();
43 LineString lineGeometry = new LineString(newList);
44
45 // AddFeature creates the point and a row in the DataTable
46 return lineFeatureSet.AddFeature(lineGeometry);
47 }
48
49 private void StartOrContinueDrawingPath(System.Drawing.Point mouseLocation)
50 {
51 Coordinate coord = map.PixelToProj(mouseLocation);
52
53 if (_LineFeature == null)
54 {
55 // This is the first time we see a left click; create empty line feature.
56 _LineFeature = AddLineFeatureSetToMap();
57
58 // Add first coordinate to the line feature.
59 _LineFeature.Coordinates.Add(coord);
60
61 // Set the line feature attribute. This line may have multiple points,
62 // but there is only one row in the attribute table for the entire feature (line).
63 _LineFeature.DataRow["ID"] = 0;
64 _LineFeature.DataRow["Description"] = "Path (line)";
65 }
66 else
67 {
68 // Second or later click - add points to the existing feature
69 _LineFeature.BasicGeometry.Coordinates.Add(coord);
70 _LineFeature.ParentFeatureSet.InitializeVertices();
71
72 // Draw the line.
73 map.ResetBuffer();
74
75 // Update the attribute table.
76 _LineFeature.DataRow["Number of Points"] = _LineFeature.BasicGeometry.Coordinates.Count;
77 }
78 }
79
80 private void EndDrawingPath()
81 {
82 // The path is complete.
83 map.ResetBuffer();
84 map.Cursor = Cursors.Arrow;
85 map.MouseDown -= map_MouseDown;
86 _LineFeature = null;
87 }