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:
public partial class DesktopMapping : Form
{
private AppManager _AppManager;
public DesktopMapping()
{
InitializeComponent();
_AppManager = new AppManager();
_AppManager.LoadExtensions();
}
private void uxOpenFile_Click(object sender, EventArgs e)
{
uxMap.AddLayer();
}
private void uxZoomIn_Click(object sender, EventArgs e)
{
uxMap.ZoomIn();
}
private void uxZoomWide_Click(object sender, EventArgs e)
{
uxMap.ZoomToMaxExtent();
}
private void uxPan_Click(object sender, EventArgs e)
{
uxMap.FunctionMode = DotSpatial.Controls.FunctionMode.Pan;
}
private void uxGenShp_Click(object sender, EventArgs e)
{
var rnd = new Random();
var pg = new Polygon[100];
var f = new Feature();
var fs = new FeatureSet(f.FeatureType);
for (int i = 0; i < 100; i++)
{
var center = new Coordinate((rnd.Next(50) * 360) - 180, (rnd.Next(60) * 180) - 90);
var coord = new Coordinate[50];
for (int ii = 0; ii < 50; ii++)
{
coord[ii] = new Coordinate(center.X + Math.Cos((ii * 10) * Math.PI / 10), center.Y + (ii * 10) * Math.PI / 10);
}
coord[35] = new Coordinate(coord[0].X, coord[0].Y);
pg[i] = new Polygon(coord);
fs.Features.Add(pg[i]);
}
fs.SaveAs("C:\\Temp\\test.shp", true);
}
}
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.
public void Plot(double[] data) {
chart1.Series.Clear(); var series = chart1.Series.Add("Elevation (meters)"); series.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; series.Points.DataBindY(data);
}
Raszter adatok kinyerése
A DotSpatial segítségével könnyedén kinyerhetjük a szükséges magassági adatokat.
private static double GetElevation(IMapRasterLayer raster, Coordinate coordinate) {
RcIndex rowColumn = raster.DataSet.Bounds.ProjToCell(coordinate); double elevation = raster.DataSet.Value[rowColumn.Row, rowColumn.Column]; return elevation;
} private static double GetDistance(double x1, double y1, double x2, double y2) {
return Math.Sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
}
Koordináták kinyerése
private List<Coordinate> GetCoordinatesFromLine(IMapLineLayer lineLayer) {
IFeatureSet featureSet = lineLayer.DataSet;
// The coordinates should be the first feature of the feature set. IList<Coordinate> lineCoordinates = featureSet.Features[0].Coordinates;
// Though the original line may only have a few points, we split // each line segment into many points List<Coordinate> pathCoordinates = new List<Coordinate>();
for (int i = 0; i < lineCoordinates.Count - 1; i++) { Coordinate startCoord = lineCoordinates[i]; Coordinate endCoord = lineCoordinates[i + 1]; List<Coordinate> segmentCoordinates = SplitSegment(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y);
//add list of points from this line segment to the complete list pathCoordinates.AddRange(segmentCoordinates); } return pathCoordinates;
}
private static List<Coordinate> SplitSegment(double startX, double startY, double endX, double endY) {
const int MinimumDistanceBetweenPoints = 15;
double points = Math.Floor(GetDistance(startX, startY, endX, endY) / MinimumDistanceBetweenPoints); int PointsPerSegment = (int)Math.Max(points, 1);
double curX = startX; double curY = startY; double constXdif = ((endX - startX) / PointsPerSegment); double constYdif = ((endY - startY) / PointsPerSegment);
List<Coordinate> pathPointList = new List<Coordinate>(PointsPerSegment); for (int i = 0; i <= PointsPerSegment; i++) { if (i == 0) { curX = startX; curY = startY; } else { curX = curX + constXdif; curY = curY + constYdif; } Coordinate coordinate = new Coordinate(curX, curY); pathPointList.Add(coordinate); } return pathPointList;
}
private void ShowElevation() {
if (!map.GetRasterLayers().Any()) { MessageBox.Show("Please add a DEM raster layer to the map."); return; }
if (!map.GetLineLayers().Any()) { MessageBox.Show("Please create a path by left clicking to add points and right-clicking to complete the path."); return; }
try { IMapRasterLayer rasterLayer = map.GetRasterLayers().First(); IMapLineLayer pathLayer = map.GetLineLayers().First(); var coords = GetCoordinatesFromLine(pathLayer);
double[] elevation = new double[coords.Count]; for (int i = 0; i < coords.Count; i++) { elevation[i] = GetElevation(rasterLayer, coords[i]); }
ChartForm chart = new ChartForm(); chart.Plot(elevation); chart.Show(); } catch (Exception ex) { MessageBox.Show("Error calculating elevation. The whole path should be inside the DEM area. " + ex.Message); }
}
SimpleActionItem használata (menüelem)
public void ButtonClick(object sender, EventArgs e) {
// We're expecting this extension to only be run in a Windows Forms application. // We'll depend on a few Windows Forms (Map) features like MouseDown, so we cast // the App.Map as a Map and store a reference to it. map = App.Map as Map;
// remove any existing path if needed. if (_PathLineLayer != null) map.Layers.Remove(_PathLineLayer);
_PathLineLayer = null; _LineFeature = null;
// Let the user know we are ready for them to set points by changing the cursor. map.Cursor = Cursors.Cross; map.MouseDown += map_MouseDown;
}
Vektor réteg létrehozása
private void map_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) { // Encourage the user to select a raster, if they haven't done so. if (!map.GetRasterLayers().Any()) { map.AddRasterLayer(); map.ZoomToMaxExtent(); return; }
StartOrContinueDrawingPath(e.Location); App.ProgressHandler.Progress(null, 0, "Point registered. Click again to add line segment. Right-click to finish."); } else if (e.Button == MouseButtons.Right) { EndDrawingPath(); ShowElevation(); App.ProgressHandler.Progress(null, 0, "Ready."); }
}
private IFeature AddLineFeatureSetToMap() {
FeatureSet lineFeatureSet = new FeatureSet(FeatureType.Line); lineFeatureSet.Projection = map.Projection;
// Initialize the featureSet attribute table by creating columns DataColumn column = new DataColumn("ID", typeof(short)); lineFeatureSet.DataTable.Columns.Add(column); DataColumn column2 = new DataColumn("Number of Points", typeof(int)); lineFeatureSet.DataTable.Columns.Add(column2); DataColumn column3 = new DataColumn("Description"); lineFeatureSet.DataTable.Columns.Add(column3);
// Add the featureSet as map layer _PathLineLayer = (MapLineLayer)map.Layers.Add(lineFeatureSet); _PathLineLayer.Symbolizer = new LineSymbolizer(Color.Blue, 2); _PathLineLayer.LegendText = "Path Layer";
var newList = new List<Coordinate>(); LineString lineGeometry = new LineString(newList);
// AddFeature creates the point and a row in the DataTable return lineFeatureSet.AddFeature(lineGeometry);
}
private void StartOrContinueDrawingPath(System.Drawing.Point mouseLocation) {
Coordinate coord = map.PixelToProj(mouseLocation);
if (_LineFeature == null) { // This is the first time we see a left click; create empty line feature. _LineFeature = AddLineFeatureSetToMap();
// Add first coordinate to the line feature. _LineFeature.Coordinates.Add(coord);
// Set the line feature attribute. This line may have multiple points, // but there is only one row in the attribute table for the entire feature (line). _LineFeature.DataRow["ID"] = 0; _LineFeature.DataRow["Description"] = "Path (line)"; } else { // Second or later click - add points to the existing feature _LineFeature.BasicGeometry.Coordinates.Add(coord); _LineFeature.ParentFeatureSet.InitializeVertices();
// Draw the line. map.ResetBuffer();
// Update the attribute table. _LineFeature.DataRow["Number of Points"] = _LineFeature.BasicGeometry.Coordinates.Count; }
}
private void EndDrawingPath() {
// The path is complete. map.ResetBuffer(); map.Cursor = Cursors.Arrow; map.MouseDown -= map_MouseDown; _LineFeature = null;
}