SpatiaLite Formatı

SpatiaLite, coğrafi veri yönetimi için (depolama, sorgulama, editleme, analiz, import/export) Alessandro Furieri tarafından Open Geospatial Consortium (OGC) Simple Feature For SQL standartlarına uygun olarak tasarlanarak ilk versiyonu 2008 yılında yayınlanmış bir SQLite veritabanı eklentisidir.

SpatiaLite Linux, Windows ve MAC platformlarında çalışabilir. Komut satırı, grafik arabirim ve ayrıca Python, JAVA, C++ ve C# için geliştirilen kütüphaneler kullanılarak yönetilebilir.

SpatiaLite ve beraberinde geliştirilen diğer uygulamalara http://www.gaia-gis.it/gaia-sins/ adresinden erişilebilir. Uygulamalar kaynak kodu indirilerek derlenebileceği gibi (Readme dosyasında windows, mac ve linux platformlarında nasıl derleme yapılacağı anlatılmaktadır) önceden derlenmiş olarak da edinilebilir.

MAC ortamında Kurulum ke kullanımını inceleyelim,

Komut Satırı Üzerinden Kullanım
Terminalde brew install spatialite-tools komutu çalıştırılır. Yükleme işlemi başarıyla tamamlandıktan sonra SpatiaLite ve birlikte gelen diğer CLI kütüphanelerini de terminal üzerinden kullanarak veritabanı işlemleri yapılabilir. (paket yöneticisi üzerinden indirilen dosyala disk üzerinde /usr/local/Cellar klasörüne kaydedilir.)

--versiyon öğrenme
spatialite -version  
--yeni veritabanı olusturma
spatiallite> test.db
--tablo olusturma
spatialite> CREATE TABLE test_poi (
  id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL);
-- Tabloya geometri sütunu ekleme
spatialite> SELECT AddGeometryColumn('test_poi', 'geom',4326, 'POINT', 'XY');

spatialite_cli

spatialite-tools, spatialite ile birlikte geliştirilen komut satırı araçlarına verilen isimdir. Bu araçlarla genelde shape, gml, dxf, osm formatlarındaki verilerin import ve/veya export işlemleri yapılmaktadır.

GUI Üzerinden Kullanım
Görsel arabirim üzerinden yönetim işlemlerini yapmak için terminalde brew install spatialite-gui komutu çalıştırılır. Yükleme işlemi başarıyla tamamlandıktan sonra terminalde spatialite_gui komutu ile arabirime ulaşabiliriz.

spatialite_gui

Görsel arabirim üzerinden import, export, geometri görüntüleme, sorgular, bakım vb veritabanı işlemleri rahatlıkla yapılabilmektedir.

API Üzerinden Kullanım
SpatiaLite için Python, C#, C++ ve Java dilinde geliştirilmiş kütüphaneler üzerinden veritabanı işlemlerimizi yapabiliriz.

Python 2 sürümleri için pyspatialite kütüphanesini kullanabiliriz.
Terminal de pip install pyspatialite ile kütüphane yüklenir.

İndirme sırasında “cannot find proj_api.h, bailing out” hata mesajı alırsanız
pip install git+https://github.com/lokkju/pyspatialite.git#egg=pyspatialite;
komutu ile indirmeyi denyin

from random import uniform
import pyspatialite.dbapi2 as db
import os

def random_point():
   return  uniform(-90, 90),uniform(-180,180)

filename = 'test_db.sqlite'
try:
    os.remove(filename)
except OSError:
    pass

conn = db.connect('test_db.sqlite')
cur = conn.cursor()

rs = cur.execute('SELECT sqlite_version(), spatialite_version()')
for row in rs:
    msg = "> SQLite v%s Spatialite v%s" % (row[0], row[1])
    print msg

# creating a POINT table
sql = 'CREATE TABLE test_pt ('
sql += 'id INTEGER NOT NULL PRIMARY KEY,'
sql += 'name TEXT NOT NULL)'
cur.execute(sql)

# creating a POINT Geometry column
sql = "SELECT AddGeometryColumn('test_pt','geom', 4326, 'POINT', 'XY')"
cur.execute(sql)

# inserting some POINTs
for i in range(100):
    p = random_point();
    name = "test POINT #%d" % (i+1)
    geom = "GeomFromText('POINT("
    geom += "%f " % p[0]
    geom += "%f" % p[1]
    geom += ")', 4326)"
    sql = "INSERT INTO test_pt (id, name, geom) "
    sql += "VALUES (%d, '%s', %s)" % (i+1, name, geom)
    cur.execute(sql)
conn.commit()

# display rows
sql = "SELECT id,name,st_astext(geom) FROM test_pt"
rs = cur.execute(sql)
for row in rs:
    print row[0],row[1],row[2]

Avantajları

  • Cross platform çalışabilmesi
  • Open source olması
  • Tüm dosyaları (index,data) tek bir dosya içerisinde tutması
  • Portable olması
  • Veritabanı işlemlerini (table, view, join, query, trigger vb) yapabiliyor olması
  • Spatial index oluşturulabilmesi
  • Zengin import/export seçenekleri
  • Virtual table desteği sayesinde shape ve csv gibi dosyaları import etmeden veri tabanındaki bir tablo gibi kullandırabilmesi
  • Büyük miktarda veri tutabilmesi
  • Çoklu Spatial Reference Desteği
  • Spatial Analiz (Overlaps, Touches, Union, Buffer) ve Spatial Function (AsText, GeomFromText, Area, PointN) desteği

SptiaLite QGIS içerisinde doğrudan kullanılabilmektedir. GeoServer ve OpenJump yazılımlarında pluginler üzerinden desteklenmektedir. Kullanım yoğunluğu her ne kadar shape file kadar olmasa da, avantajları dikkate alındığında kesinlikle göz önünde bulundurulması gereken bir formattır.

Sağlıklı ve huzurlu günler dilerim.

Run Multiple Parallel Process And Wait For Completion

Yük testi yaparken veya uzun süren  işlemlerde multi thread uygulamalar geliştirerek çözüm üretebiliyoruz. Ancak bazen multithreading tekniği ile ürettiğimiz çözümler yeterli gelmeyebilir. Ya da thread synchronization, cross thread access, dead lock gibi zorluklarla uğraşmadan çözüm üretmek isteyebiliriz. Bu gibi durumlarda işi Host Application üzerinden çalıştırılan birden fazla Child Application’a paylaştırarak alternatif bir çözüm sağlayabiliriz.

Host Application
Görevi yapılacak işi birden fazla Child Application’a paylaştırmaktır. İş Child Application’lara argument üzerinden aktarılmaktadır. Komut satırından geçilebilecek argument’in uzunluk sınırlaması olduğu için argüman binary serialization işleminden sonra aktarılır. Host Application, tüm Child Application’ları paralel olarak aynı anda başlatır ve tamamı sonlanana kadar bekler.

Child Application
Argüman üzerinden gelen veriye göre kendi işini yapar.

Örnek senaryomuzda elimizde büyük miktarda resim url’i bulunmaktadır. Resimleri en kısa zamanda indirip diske kaydetmek isteyelim.Host Application resim url’lerini dosyadan okur ve Child Application’lara paylaştırır. Child Application’lar kendilerine oaylaştırılan url listesini indirerek diske kaydeder.

Host Application

class Program
{
    static List<T>[] Partition<T>(List<T> list, int totalPartitions)
    {
        if (list == null)
            throw new ArgumentNullException("list");

        if (totalPartitions < 1)
            throw new ArgumentOutOfRangeException("totalPartitions");

        List<T>[] partitions = new List<T>[totalPartitions];

        int maxSize = (int)Math.Ceiling(list.Count / (double)totalPartitions);
        int k = 0;

        for (int i = 0; i < partitions.Length; i++)
        {
            partitions[i] = new List<T>();
            for (int j = k; j < k + maxSize; j++)
            {
                if (j >= list.Count)
                    break;
                partitions[i].Add(list[j]);
            }
            k += maxSize;
        }

        return partitions;
    }

    private static string Serialize<T>(T obj)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(stream, obj);
            stream.Flush();
            stream.Position = 0;
            return Convert.ToBase64String(stream.ToArray());
        }
    }
    static void Main(string[] args)
    {
        try
        {
            string fileName = "urllist.txt";
            int subProcessCount = 10;
            var urlList = File.ReadAllLines(fileName).ToList();
            var jobList = Partition(urlList, subProcessCount);
            var workingDirectory = Environment.CurrentDirectory;
            var subProcessName = "ChildApp.exe";
                
            Console.WriteLine("Start");
            Parallel.For(0, subProcessCount, i =>
            {
                var arg = new ProcessArg();
                arg.UrlList = jobList[i];
                var argString = Serialize(arg);
                Console.WriteLine("Child process {0} started", i);
                CreateChildProcess(workingDirectory, subProcessName, argString);
            });
            Console.WriteLine("All processes are completed");
            Console.Read();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        Console.Read();
    }
    private static void CreateChildProcess(string workingDirectory,
        string jobProcessName, string jobProcessArg)
    {
        var process = new Process();
        process.StartInfo.FileName = jobProcessName;
        process.StartInfo.WorkingDirectory = workingDirectory;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.Arguments = jobProcessArg;
        process.Start();
        process.WaitForExit();
    }
}

ChildApp

class Program
{
    private static T Deserialize<T>(string objStr)
    {
        byte[] b = Convert.FromBase64String(objStr);
        using (var stream = new MemoryStream(b))
        {
            var formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
    static void Main(string[] args)
    {

        string strArg = args[0];
        var arg = Deserialize<ProcessArg>(strArg);
        for (int i = 0; i <= arg.UrlList.Count; i++)
        {
            var uri = new Uri(arg.UrlList[i]);
            var localFileName = System.IO.Path.GetFileName(uri.LocalPath);
            try
            {
                WebClient webClient = new WebClient();
                webClient.DownloadFile(uri, localFileName);

                Console.WriteLine(localFileName + "...ok");
            }
            catch (Exception ex)
            {
                Console.WriteLine(localFileName + "...err" + ex.Message);
            }

        }

    }
}

Bu şekilde kod karmaşıklığından kurtularak yüksek performans elde edebiliyoruz. Tüm child processler tamamlandığında host uygulamanın ekran görüntüsü aşağıdaki gibi olur.

childprocess.png

Uygulamaya ait kaynak kodları buradan indirebilirsiniz.

Sağlıklı ve huzurlu günler dilerim

Kaynaklar

GeoJSON Formatı

İlk olarak 2008 yılında tanımlanmıştır. 2015 yılında, İnternet Mühendisliği Görev Gücü (IETF), orijinal formatın yazarlarıyla birlikte GeoJSON’u standartlaştırmak için bir GeoJSON çalışma gurubu oluşturdu. Bu gurubun çalışması RFC 7946, Ağustos 2016’da yayınlandı ve 2008 GeoJSON formatının yerini alan yeni GeoJSON standartı oluşturulmuş oldu. Adından anlaşılacağı gibi javascript nodasyonunu kullanan JSON tabanlı bir coğrafi veri formatıdır. Simple ve Multi Part Geometri, meta data ve projeksiyon tanımlanmasını desteklerken style tanımını desteklemez. Formatla ilgili detaylara http://geojson.org/ adresinden erişilebilir.

JSON tabanlı olduğu için dosya boyutu oldukça küçüktür. Bu özelliği dolayısı ile mobile platformlarda ve web de yoğun olarak tercih edilir. Google Maps JavaScript API v3 de GeoJSON formatındaki veri loadGeoJson metodu ile doğrudan kullanılabilmektedir. GeoJSON formatı ayrıca OpenLayers ve LeafLet gibi kütüphanelerde de tercih edilmektedir.

geojson.io sitesinde online görüntüleme ve editleme yapılabilmektedir.

https://parselsorgu.tkgm.gov.tr/ adresinden export edilen örnek geojson verisi

{
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              32.66443206373,
              39.947024611095
            ],
            [
              32.664705019776,
              39.946774069094
            ],
            [
              32.664534209976,
              39.946664574481
            ],
            [
              32.664397007801,
              39.946790563768
            ],
            [
              32.664259316154,
              39.946917001784
            ],
            [
              32.66443206373,
              39.947024611095
            ]
          ]
        ]
      },
      "properties": {
        "ParselNo": "5",
        "Alan": "683,00 m2",
        "Mevkii": "Karabaş ve bağlıca sokağı",
        "Nitelik": "Apartman - beton",
        "Ada": "8647",
        "Il": "Ankara",
        "Ilce": "Etimesgut",
        "Pafta": "420",
        "Mahalle": "Etimesgut"
      }
    }
  ],
  "type": "FeatureCollection",
  "crs": {
    "type": "name",
    "properties": {
      "name": "EPSG:4326"
    }
  }
}

Leaflet üzerinde geojson gösteren örnek uygulama ile konuyu sonlandıralım.

örnek uygulama html’i

<!DOCTYPE html>
<html>
<head>
    <title>Leaflet Show GeoJson Demo</title>

    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <!-- Leaflet CSS -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css"/>
    <!--Style.css-->
    <link rel="stylesheet" href="style.css">

</head>

<body>
<!-- Modal -->
<div class="modal fade" id="geo-json-modal" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="exampleModalLabel">GeoJon Ipnput</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <form>
                    <div class="form-group">
                        <label for="layer-name-input" class="col-form-label">Layer Name</label>
                        <input type="text" class="form-control" id="layer-name-input">
                    </div>
                    <div class="form-group">
                        <label for="geo-json-input" class="col-form-label">GeoJson:</label>
                        <textarea class="form-control" id="geo-json-input" style="height: 150px"></textarea>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-primary" id="add-map">Show On Map</button>
            </div>
        </div>
    </div>
</div>

<h2>GeoJSON Viewer Demo</h2>
<div id="map"></div>
<div style="margin-top:20px">
    <button id="draw" type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#geo-json-modal">Load GeoJSON</button>
</div>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<script src="leaflet.control.geojson.js"></script>
</body>

</html>

örnek uylama css’i

html {
    background: #444;
}

body {
    margin-left: 100px;
    margin-top: 50px;
    background-color: #444;
}

h2 {
    color: white;
}

#map {
    width: 900px;
    height: 400px;
}

Örnek uygulama javascript’i

var geoJsonViewer = (function ($, L) {
    return {
        _addButton: null,
        _modal: null,
        _geoJsonInput: null,
        _layerNameInput: null,
        _map: null,
        _layerControl: null,

        init: function () {
            this._addButton = $("#add-map");
            this._modal = $("#geo-json-modal");
            this._geoJsonInput = $("#geo-json-input");
            this._layerNameInput = $("#layer-name-input");

            var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
                attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            });

            var esriWorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
                attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
            });

            this._map = L.map("map", {
                center: [38.95, 35.63],
                zoom: 5,
                layers: [osm, esriWorldImagery]
            });


            var baseLayers = {
                "Open Street Map": osm,
                "ESRI World": esriWorldImagery
            };

            var overlays = {};

            this._layerControl = L.control.layers(baseLayers, overlays);
            this._map.addControl(this._layerControl);
            this._addButton.on("click", $.proxy(this.addGeometry, this));

        }, addGeometry: function (e) {
            e.preventDefault();

            var text = this._geoJsonInput.val();
            var layerName = this._layerNameInput.val();

            var layer = L.geoJSON($.parseJSON(text), {
                onEachFeature: function (feature, layer) {
                    var popupContent = "";
                    for (var prop in feature.properties) {
                        popupContent += "<b>" + prop + "</b>: " + feature.properties[prop] + "</br>";
                    }
                    layer.bindPopup(popupContent);

                }
            });
            this._map.addLayer(layer);
            this._layerControl.addOverlay(layer, layerName);
            this._map.fitBounds(layer.getBounds());
            this._modal.modal("hide");
        }
    };
})(jQuery, L);

geoJsonViewer.init();

Örnek uygulamayı indirmek için tıklayınız

Sağlıklı ve huzurlu günler dilerim

Geographic Markup Language (GML)

GML, geometri, koordinat, referans sistemi gibi temel coğrafi yapıların şemalar kullanılarak tanımlanması için 1999 yılından bu yana Open Geospatial Consortium (OGC) tarafından geliştirilen standarta verilen isimdir. GML 1 versiyonunda Document Type Definition (DTD), sonraki versiyonlarda ise XML Schema Definition (XSD) dosyaları ile şemalar oluşturulmuştur. En son 3.3 versiyonu yayınlanmıştır.

GML 1-2 versiyonlarında yalnızca 3 şema bulunuyordu (gml.xsd, feature.xsd ve geometry.xsd) ve bu şemalar 2 boyutlu geometri tanımlamasına yönelik bilgiler içeriyordu. GML 3 versiyonu ile birlikte 3 boyutlu geometri, topoloji, coverage gibi konular da dahil edilerek 30’un üzerinde şema tanımlanmıştır.

GML şema tanımlamalarına http://schemas.opengis.net/gml/ adresinden erişilebilir.

Application Schemas
Temel GML şemalarını kullanarak tanımlanan şemalardır. CityGML, GeoRSS gibi genel bir ihtiyaca yönelik olabileceği gibi, daha spesifik bir ihtiyaç için de tanımlanabilir. Application Schema’lar tanımlayan tarafından verilen isim alanı (namespace) altında yaşarlar.

GML Profiles
GML içerisinde tanımlanmış tüm şemaların kullanımına ihtiyacımız olmayacağı için Point Profile, Feature Profile gibi GML’in alt kümesi olan tanımlamalara ihtiyaç duyulmuştur. Böylece kendi şemalarımızı daha kolay bir şekilde profilleri kullanarak tanımlayabiliriz. GML Profil’ler GML (http://www.opengis.org/gml) isim alanı (namespace) altında yaşarlar.

Şehir merkezlerini tutmak için kendi GML şema tanımızı yapalım. İhtiyaç duyduğumuz Point ve SRS tiplerini Point Profile şemasını import ederek kullanabiliriz. Böylece bu tipler için gereksiz efor harcamamış oluyoruz. Ayrıca standartlarada uymuş oluyoruz. Yalnız kendi domainimizde ihtiyaç duyulan tipleri tanımlayarak şemayı oluşturalım.

schema.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema  targetNamespace="http://huseyinkucuk.net"
            xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:my="http://huseyinkucuk.net"
            xmlns:gml="http://www.opengis.net/gml"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">

  <!--GML Point Profile İmport Edilir-->
  <xs:import namespace="http://www.opengis.net/gml"
             schemaLocation="gml311PointProfile.xsd"/>

  <!--Kendi Tanimlarimiz Yapilir-->
  <xs:element name="Sehirler">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="my:Sehir" maxOccurs="unbounded" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:element name="Sehir">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="ad" type="xs:string"/>
        <xs:element name="bolge" type="my:Bolgeler"/>
        <xs:element name="merkez">
          <xs:complexType>
            <xs:sequence>
              <xs:element ref="gml:Point"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

  <xs:simpleType name="Bolgeler">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Trakya"></xs:enumeration>
      <xs:enumeration value="İç Anadolu"></xs:enumeration>
      <xs:enumeration value="Akdeniz"></xs:enumeration>
      <xs:enumeration value="Ege"></xs:enumeration>
      <xs:enumeration value="Karadeniz"></xs:enumeration>
      <xs:enumeration value="Doğu Anadolu"></xs:enumeration>
      <xs:enumeration value="Güney Doğu Anadolu"></xs:enumeration>
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

data.xsd

<?xml version="1.0" encoding="UTF-8"?>
<Sehirler xmlns="http://huseyinkucuk.net"
            xmlns:gml="http://www.opengis.net/gml"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://huseyinkucuk.net schema.xsd">
  <Sehir>
    <ad>Trabzon</ad>
    <bolge>Karadeniz</bolge>
    <merkez>
      <gml:Point srsDimension="2"
           srsName="urn:ogc:def:crs:EPSG:6.6:4326">
        <gml:pos>
          39.8098994041092 40.8052317836992
        </gml:pos>
      </gml:Point>
    </merkez>
  </Sehir>
  <Sehir>
    <ad>Ankara</ad>
    <bolge>İç Anadolu</bolge>
    <merkez>
      <gml:Point srsDimension="2"
           srsName="urn:ogc:def:crs:EPSG:6.6:4326">
        <gml:pos>
          32.5829637387285 39.8042922218373
        </gml:pos>
      </gml:Point>
    </merkez>
  </Sehir>
</Sehirler>

Örnek projeyi buradan indirebilirsiniz.

Sağlıklı ve huzurlu günler dilerim

Gis Data Types

Bu makalede coğrafi detayları tanımlamak için kullanılan temel veri tipleri ve bu veri tiplerinin hangi durumlarda kullanılması gerektiği anlatılacaktır.

CBS’nin diğer bilgi sistemlerinden farkı coğrafi olarak referanslanan verileri işleyebilmesidir. Coğrafi veri, yeryüzündeki mekansal özelliğin hem konumunu hem de özelliklerini tanımlamalıdır. CBS bu nedenle iki coğrafi veri bileşeni içerir.

  • Mekansal veriler, coğrafi özelliğin konumunu tanımlar
  • Öznitelik verileri (Konumun meta data bilgisi), konuma ait diğer özellikler tanımlar.

Bu veri yapısı sayesinde meta data üzerinden yapılan sorgulamalar ile konum bilgisine, konum üzerinden yapılan sorgulamalarla da meta data bilgisine erişilebilir. Mekansal veriler de temel olarak vektör ve raster olmak üzere iki kategoriye ayrılır.

VEKTÖR VERİLER

Nokta, çizgi, çokgen veya bunların kombinasyonu yoluyla herhangi bir coğrafi özelliği koordinat sistemi üzerinden ifade edebilen verilerdir. Kuyu, ağaç, kule gibi veriler POINT; yol, nehir, köprü gibi en az iki noktadan oluşan ve birbirine bir çizgi ile bağlı veriler LINE; bina, parsel, ülke sınırları gibi en az üç noktadan oluşan ve kapalı alan olarak tanımlanan veriler de POLIGON geometrileri ile temsil edilir.

Geometri Tanımlama
Vektörel geometri nesnelerinin tanımının standartlaştırılması, dijital ortamda saklanması, taşınabilmesi veya harita üzerinde gösteriminin sağlanması için Open Geospatial Consortium (OGC) ve International Organization for Standardization (ISO) tarafından işaretleme dili kullanılarak tanımlanan metin tabanlı standarta WKT (Well Known Text) denir. WKB (Well Known Binary) ise bu standartın ikili karşılığıdır. Bu standart da geometriler basit ve multi part olarak aşağıdaki gibi tanımlanabilir.

POINT   POINT(10 10)
MULTIPOINT   MULTIPOINT ((10 40), (40 30), (20 20), (30 10))
LINESTRING   LINESTRING (30 10, 10 30, 40 40)
MULTILINESTRING   MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))
POLYGON   POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))
MULTIPOLYGON   MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))

Tabloda görüleceği üzere geometriler koordinatlardan oluşmaktadır. Koordinatlar (X Y) , (X Y Z) veya (X Y Z M) şeklinde tanımlanabilmektedir.

Burada;

  • X yatay (easting), Y (northing) ise dikey değeri
  • Z Yükseklik (elevation)
  • M Ölçüm zamanı, Sıcaklık, Basınç vs gibi kendimizin belirleyebileceği herhangi bir değeri ifade eder.
  • Projected koordinat sistemlerde değerler X ve Y olarak, coğrafi koordinat sistemlerinde ise Longitute ve Latitude olarak olarak isimlendirilir.

Axis Ordering
Koordinatların sıralaması konusunda bazı karışıklıklar bulunmaktadır. Örneğin
WFS 1.0 standartlarında koordinatlar longitude / latidute sıralaması ile gönderilirken, WFS 1.1 ve 2.0 sürümünde latitude / longitude sıralamasına geçilmiştir. GeoJSON veri formatında ise koordinatlar (longitude / latidute ) formatında gönderilmektedir. CRS (Coordinat Reference System) tanımlama dosyalarında koordinat sıralamasının da belirtilmesi ile bu konu standartlaştırılmaya çalışılmaktadır.

Vektör Veri Üretimi
Temel olarak iki farklı yöntemle vektör veri üretilebilir.

  • Coğrafi referanslama (Georeferancing) işlemi yapılmış hava fotoğrafı veya uydu görüntüleri üzerinde çizim araçlarını kullanarak sayısallaştırma yöntemi ile
  • Özel ölçüm yöntemleri (GPS, TotalStation vs) ile araziden toplanarak

Vektör Verilerin Avantajları

  • Grid büyüklüğüne bağlı olmadan koordinatlardan oluştukları için daha yüksek bir coğrafi doğruluğa sahiptirler.
  • Grafiksel çıktıları daha estetiktir.
  • Vektör veriler ile topoloji kurulabilir ve bu topolojik kurallar sayesinde veri tutarlılığı sağlanır.
  • Vektör veriler bozulmadan ölçeklendirilebilirler
  • Birden çok özellik ile ilişkilendirilebilirler.
  • Raster veri modeline göre çok daha küçük veri boyutuna sahiptir.

Vektör Veri Analizleri

  • Network analizi : Yol, kanalizasyon, su veya elektrik şebekesi gibi ağ oluşturan detaylar üzerinde yapılan en kısa yol, kaynak tahsisi, etki alanı analizleri
  • Yakınlık analizi : Belirli bir alan içerisinde istenilen kriterlere uyan detaylar. (Oturduğum yerin 1 km yakınındaki okullar)
  • Kesişim, içinde kalma, değme vs gibi geometrik analizler

RASTER VERİLER

Dijital kameralar ile çekilen uydu görüntüleri ile hava fotoğrafları veya taranarak bilgisayara aktarılan dökümanlar en bilinen ve kullanılan raster verilerdir.

Raster veri tipinde, nokta, çizgi veya poligon kavramları yoktur. Raster veriler piksellerden oluşur. Diğer bir ifade ile raster, sütunlar ve satırlar içeren ızgara hücre dizisidir.

Raster verinin temel kullanım amacı altlık haritadır. Böylece
Raster veri görüntüleme kontrol ve analiz için kullanılır.Üretilen vektör verilerin konumunun görüntülenmesi ve kontrol edilmesi sağlanır.

Coğrafi Referanslama (Georeferancing)
Raster’ın pixel koordinatlarının doğru parametreler kullanılarak dünya üzerinde olması gereken yere yerleştirilmesi işlemine coğrafi referanslama veya konumlandırma denir. Konumlandırma işlemi için ihtiyaç duyulan parametreler 3 farklı şekilde elde edilebilir.

  • World File
    World file dosyaları raster ile aynı dizinde yer alır ve ismlendirilirken raster ile aynı adı alır. Uzantısı ise raster uzantısının 1. ve 3. harfleri ile “w” harfinin birleşiminden oluşur. Örneğin raster dosyasının adı kıbrıs.tif ise world file dosyası kıbrıs.tfw, keban.jpg ise keban.jgw olur.

    World File dosyasının içeriği 6 satırdan oluşur. Rasterin sol üst köşe koordinatı, x ve y ekseni boyunca dönüklük miktarı ve piksel büyüklüğüne ait değerleri içerir.

  • Raster Header
    GeoTiff gibi bazı raster formatları konumlandırma işlemi için ihtiyaç duyulan parametreleri raster dosyasının header kısmında barındırır.
  • Identity Transformation
    Ekran üzerinden operatör yardımı ile başka bir referanslanmış veriyi kullanarak da coğrafi referanslama yapılabilir.

Çözünürlük
Raster veri için çözünürlük kavramı 4 değerden oluşmaktadır.

  • Mekansal Çözünürlük (Spatial Resolution)
    Her bir pikselin büyüklüğü mekansal çözünürlük kavramı ile açıklanır. 10 metre çözünürlükte bir raster görüntü de her bir piksel gerçek dünyada 10 metreye 10 metre bir alanı temsil eder. Bu değer aynı zamanda raster’ın 10 m den küçük değişimleri ortaya koymak için yeterli hassasiyet olmadığı anlamına gelir. Bir nesne ne kadar çok piksel ile temsil edilir ise görüntü kalitesi o kadar fazla olur.

    enter image description here

  • Spektral Çözünürlük (Spectral Resolution)
    Spektral dalga boyu genişliği bir algılayıcının elektromanyetik spektrumda kaydedebildiği spesifik dalga boyu aralığıdır. Elektromanyetik spektrumdaki geniş aralıklar düşük spektral çözünürlük, dar aralıklar ise yüksek spektral çözünürlük olarak tanımlanır. Yani bant aralığı küçüldükçe spektral çözünürlük artmaktadır. Bunun yanısıra bant sayısı arttıkça spektral çözünürlükte artmaktadır. Daha fazla bant sayısı daha fazla spektral detay içerdiğinden nesnelerin ayırt edilmesinde kolaylık sağlamaktadır.
  • Radyometrik Çözünürlük (Radiometric Resolution)
    Bir algılayıcının enerji farklılıklarını algılayabilme yeteneğini ifade eder. Bit olarak tanımlanır. 1-bitlik renk derinliğine sahip bir raster iki değer içerebileceğinden dolayı görüntü siyah beyaz olur. 8 bitlik renk derinliğine sahip bir raster da her bir pixel 0-255 arasında (toplam 256 değer) değer alabililir.

    2 Bit 4 Bit 8 Bit
    enter image description here enter image description here enter image description here
  • Zamansal Çözünürlük (Temporal Resolution)
    Zamansal çözünürlük bir uzaktan algılama sisteminin aynı bölgeyi görüntüleme
    sıklığı ile ilgilidir. Diğer bir ifade ile uydunun aynı arazi parçasını üst üste iki defa algılama yapması arasında geçen süredir.

Raster verinin kalitesi inç başına düşen nokta sayısı ile (DPI) ifade edilir. Inç başına düşen piksel sayısı arttıkça raster’ın gerçek dünyadaki modeli temsil gücü ve doğruluğu artarak kalitesi yükselir. Ancak bu işlem raster’ın dosya boyutunu arttırır.

Raster Verilerin Avantajları

  • Veri yapısı basittir
  • Bazı çoğrafi analizlerin (çakıştırma analizleri,kullanım yoğunluğu gibi) kolaylıkla yapılabilir olması

Raster Tabanlı Analizler
Raster analizler hücrelerin içerdiği veri değerlerinden faydalanılarak yapılır.Raster veride bir piksel yalnızca bir değer içerir. Bu nedenle birden fazla özellik göz önüne alınacaksa, birkaç raster katman gerekir.

  • Hidrolojik Analizler
    Dere akış yönü,Yağış birikimi,Su ve drenaj havzalarının belirlenmesi,baraj ve gölet bölgeleri,sel rsiki olan alanar
  • Çevre Analizleri
    Küresel ısınma yaban hayatı,ormanların yok olması
  • Arazi Analizleri
    Görünürlük,eğim,bakı analizleri ile arazi kullanıı ve yer seçimi analizler

Kullanım amacına bağlı olarak raster veya vektör veri tipi seçilebilir. Bu veriler birbirlerine de dönüştürülebilir.

Sağlıklı ve huzurlu günler dilerim

Kaynaklar

Dependency Injection Container

Önceki yazıda uygulamaları oluşturan bileşenlerin birbiri ile gevşek bağlı haberleşmesi gerektiğini ve bunun için interface kullanılabileceğini örneklemiştik. Bu yazıda bağımlılıkları daha esnek nasıl yönetebileceğimizi inceliyor olacağız.

Dependency Injection Design Pattern
Bağımlılıkların tasarım zamanı yerine çalışma zamanında yüklenmesini tarifleyen tasarım desendir. Bu disipline uygun şekilde tasarlanan uygulamalar da tüm uygulamanın yeniden derlenmesine gerek kalmadan yalnızca değişen kısımları derlenerek uygulama güncellenebilir.

Dependency Injection Design Pattern’in kolayca uygulanabilmesi için geliştirilmiş pek çok hazır kütüphane bulunmaktadır. Dependency Injection Container olarak bilinen bu kütüphaneler konfigürasyon dosyaları veya kod üzerinden tanımlanan bağımlılıkları talep edildiğinde nesnelerimize bağlayarak (inject) kullanıma sunarlar.

Castle Windsor, Spring.Net, Ninject ve Microsoft tarafından desteklenen açık kaynak kodlu Unity Container kullanılabilecek DI kütüphaneleridir. Daha geniş bir liste için burayı ziyaret edebilirsiniz. Biz bu makalemizde Unity Container kütüphanesini kullanacağız.

Kütüphaneyi nuget üzerinden yükleyebiliriz. Yeni bir Console Uygulaması açalım ve References klasörü üzerinde Manage Nuget Packages menusüne tıklayalım.

unity-nuget-1

Browse sekmesinde unity yazıp gelen listede en üstte yer alan 5.5.2 sürümünü seçip install düğmesine tıklayalım

unity-nuget-2

Unity Application Block’u kullanabilmek için
Unity.Configuration, Unity.Abstractions, Unity.RegistrationByConvention ve Unity.Container dosyalarını projemize eklememiz gerekiyor.

unity-nuget-3

Kütüphanenin temel iki işlemi vardır. Registration ve Resolution

REGISTRATION
Bağımlılıkların kütüphaneye kaydedilmesi işlemidir. Fluent arayüz ile kod seviyesinde veya deklaratif olarak konfigürasyon dosyaları (web.config/app.conifg) üzerinden yapılabilir. Tanımlama işlemi yönetimsel kolaylığı sağlamak için tek bir noktadan (Main, App_Start) yapılması önerilmektedir. Bağımlılıkların container’a kaydedilmesi farklı şekillerde olabilir;

  • Type Registration
    RegisterType metodu ile yapılır. Container’a doğrudan bir tip kaydı yapılabileceği gibi. Bir interface’i implement etmiş veya base sınıftan türemiş bir tipin kaydı da yapılabilir.

    //Unity Container Olusturulur
    IUnityContainer container = new UnityContainer();
    //Arabirim üzerinden tanımlama
    container.RegisterType<ILogger, FileLogger>();
    //Dogrudan tanımlama
    container.RegisterType<DbLogger>();
    
  • Named Registration
    Aynı tipten türemiş birden fazla tip container’a kaydedilebilir. Bu durumda container, talep edildiğinde en son eklenen nesne örneğini döndürür. Tiplerin container’a kaydı isimlendirilebilir. Böylelik çalışma zamanında istenen tip verilen isim üzerinden yüklenebilir. Başka bir çözüm de child container’oluşturup tipleri farklı container’lara kaydedebiliriz. Bunun avantajı eğer child container’de aranan tip bulunmaz ise otomatik olarak parent container üzerindeki eşleşme döndürülür.

    IUnityContainer container = new UnityContainer();
    container.RegisterType<ILogger, FileLogger>("FileLogger");
    container.RegisterType<ILogger, DbLogger>("DbLogger");
    // ya da child container kullanabiliriz
    var childContainer = container.CreateChildContainer();
    childContainer.RegisterType<ILogger, DbLogger>();
    
  • Instance Registration
    RegisterInstance metodu ile var olan bir nesne örneği de container’a kaydedilebilir. Instance kaydı Singleton olarak gerçekleştirilir.

    IUnityContainer container = new UnityContainer();
    var constants = new Constants()
    container.RegisterInstance(constans);
    
  • Registration by Convention (Auto Registration)
    RegisterTypes metodu ile yapılan otomatik kayıt işlemi, belirlenen kurallara göre uygulamadaki bileşenleri tarar ve kurala uygun olanları container’a yükler. Böylece kayıt işlemleri ile daha az zaman harcanır. Bu tarz kayıt işlemi çok fazla tanımlamanın olduğu durumda anlamlıdır. Yalnızca birkaç tip kaydı yapılacak ise manuel tanımlamak daha doğru olur.

    //uygulama domaininde yuklu soyut olmayan tum tipler
    //icerisinde hedefnamespace altında bulunan belirtilen
    //interface'i implement etmis tipleri container'a kaydeder
    
    container.RegisterTypes(
     AllClasses.FromLoadedAssemblies().Where(
     t => t.Namespace == "HedefNamespace"),
     WithMappings.MatchingInterface);
    

LifeTimeManager
Varsayılan olarak container tip oluşturma işlemlerinde her talepte yeni bir instance oluşturur. Bu davranış LifeTimeManager nesneleri ile yönetilir. Bağımlılık singleton olarak oluşturulacaksa ContainerControlledLifetimeManager, her ihtiyaçta tekrar oluşturulacaksa ExternallyControlledLifetimeManager sınıfları kullanılır.

var singletonLifeTime = new ContainerControlledLifetimeManager();
var externallyLifetime = new ExternallyControlledLifetimeManager();
IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, FileLogger>(singletonLifeTime);
container.RegisterType<ILogger, DbLogger>(externallyLifetime);

Web projelerinde request bazında nesne oluşturmak için PerRequestLifetimeManager, Multi Thread Projelerde thread bazında nesne oluşturmak için PerThreadLifetimeManager nesneleri kullanılabilir.

Konfigürasyon Dosyası İle Bağımlılıkların Kaydedilmesi
Kod ile bağımlılık tanımlama işlemi kolay ve kullanışlı olsa da kayıt işleminde veya bileşenlerde bir değişiklik olduğunda yeniden derleme gerektirir. Bağımlılıkları ana projeden ayırıp, app.config dosyasında aşağıda belirtildiği şekilde tanımlamaları yaparak bu problemi aşabiliriz.

<configuration>
 <configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.    UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
 </configSections>
 <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
 <namespace name="Unity.Test.Types" />
 <container>
   <register type="ILogger" mapTo="FileLogger" name="fileLogger" />
   <register type="ILogger" mapTo="DbLoger" name="dbLogger" />
 </container>
 </unity>
</configuration>

Tanımlanan bağımlılıklar app.config dosyası ile aynı dizinde olmalı aksi takdirde “The given assembly name or codebase was invalid” hatası alırız

Konfigürasyon dosyasını üzerinden bağımlılıkları yüklemek için Unity.Configuration isim alanında yer alan tipler kullanılır.

IUnityContainer container = new UnityContainer();
UnityConfigurationSection unityConfig =(UnityConfigurationSection)ConfigurationManager.GetSection("unity");
unityConfig.Configure(container);

Tanımlı bağımlılıkların listelenmesi
Container nesnesinin Registrations özelliği ile tanımlar hakkında bilgi alabiliriz.

Console.WriteLine("Kayıt sayısı = {0}:",container.Registrations.Count());
foreach (ContainerRegistration item in container.Registrations)
{
 Console.WriteLine(item.GetMappingAsString());
}

RESOLUTION
Container’a eklenen bağımlılıklar’ın çalışma zamanında Constructor, Property ve metototlara enjekte edilmesi işlemidir.

  • Constructor Injection
    Bağımlılıkların yapıcı metot aracılığı ile nesneye enjekte edilmesidir. Nesnenin birden fazla yapıcı metodu varsa [InjectionConstructor] attributu ile cntainer’a hangi yapıcı metodu kullanacağı söylenir.

    public interface ILogger
    {
        void Write(string Exception);
    }
    public class FileLogger : ILogger
    {
        public void Write(string message)
        {
           Console.WriteLine("File Log = " + message);
        }
    }
    public class DbLogger : ILoger
    {
        public void Write(string message)
        {
            Console.WriteLine("Db Log = " + message);
        }
    }
    
    public class LogManager
    {
        private ILogger _logger;
    
        [InjectionConstructor]
        public LogManager(ILogger logger)
        {
            _logger = logger;
        }
    
        public LogManager(string logerName)
        {
    
        }
    
        public void Write(string message)
        {
            _loger.Write(message);
        }
    }
    
    var container = new UnityContainer();
    container.RegisterType<ILogger, FileLogger>();
    
    //container LogManager nesne ornegini olustururken
    //constructor üzerinden bağımlılık sağlanır
    var logManager = container.Resolve<LogManager>();
    logManager.Write("Test");
    

    Çalışma zamanında dinamik olarak da constructor injection yapılabilir. Bunun için InjectionConstructor nesnesi kullanılır.

    container.RegisterType<LogManager>(new InjectionConstructor(new FileLogger()));
    
    //veya
    
    container.RegisterType<ILogger, FileLogger>();
    container.RegisterType<LogManager>(new InjectionConstructor(container.Resolve<ILogger>()));
    
  • Property Injection
    Bağımlılıkların property aracılığı ile nesneye enjekte edilmesidir. Bunun için [Dependency] attribute kullanılır. Attribut’a name degeri parametre olarak geçirilerek istenilen bağımlılık enjekte edilebilir.

    var container = new UnityContainer();
    container.RegisterType<ILoger, FileLoger>("filelogger");
    container.RegisterType<ILoger, DbLoger>("dblogger");
    public class LogManager
    {
       [Dependency("filelogger")]
       public ILogManager Logger {get; set;}
    }
    //Container LogManager nesnesini olusturdugunda Logger property'sine
    //FileLogger nesnesinin bir örneğini oluşturarak atar.
    var logManager = container.Resolve<LogManager>();
    

    Çalışma zamanında dinamik olarak InjectionProperty nesnesini kullanarak da Property Injection yapılabilir.

    var container = new UnityContainer();
    //Calisma zamanında Property Injection
    container.RegisterType<LogManager>(new InjectionProperty("Logger", new FileLoger()));
    
    var logManager = container.Resolve<LogManager>();
    logManager.Write();
    
  • Method Injection
    Bağımlılıkların metot parametreleri aracılığı ile enjekte edilmesidir. Bunun için InjectionMethod attribute kullanılır.

    public class LogManager
    {
        public LogManager() 
        {
        }
        [InjectionMethod]
        public void WriteLog(ILogger logger) {
            logger.Write("Test");
        }
    }
    

    Çalışma zamanında InjectionMethod nesnesini kullanarak dinamik Method Injection yapılabilir.

    var container = new UnityContainer();
    
    //Calisma zamanında Method Injection
    container.RegisterType<LogManager>(new InjectionMethod("WriteLog", new FileLogger()));
    
    var logManager = container.Resolve<LogManager>();
    logManager.WriteLog();
    

Deferred Resolution

Bazen, bir nesneyi container’dan alıp instance oluşturma işlemini ihtiyaç duyulduğunda gerçekleştirmek gerekebilir. Bunun için Lazy kullanılır

var defaultValue = container.Resolve<Lazy<ILogger>>();
var InstanceObject = defaultValue.Value;

Sağlıklı ve huzurlu günler dilerim

Single Responsibility And Loosely Coupling Principles

Geliştirdiğimiz uygulamalar farklı sorumlulukları yerine getiren bileşenlerin bir arada çalışmasından meydana gelir. Geliştirme, bakım ve güncelleme süreçlerinde karşılaşılacak zorlukları en aza indirmek için, uygulama mimarisi bileşenler tek bir iş yapacak ve birbirleri ili gevşek ilişkide haberleşecek şekilde tasarlanmalıdır. Her bileşenin tek bir işi olması Single Responsibility Principle, bileşenler arasındaki bağların gevşek olması Loosely Copling Principle olarak adlandırılır.

public class UserService
{
    public void Register(string email, string name)
    {
        try
        {
            //Save User

            //Send Mail
            _smtpClient.SendMail("success")
        }
        catch (Exception ex)
        {
            //Error Log
            File.Write(ex.Message)
        }
    }
} 

Yukarıdaki örnekte UserService sınıfı kullanıcı kaydetme işleminin yanında mail gönderme, hata loglama sorumluluklarını da üstlenmiş durumdadır. Bu durum tek sorumluluk ilkesini ihlal eder, sorumluklardan herhangi biri değiştiğinde uygulamanın tekrar derlenmesi gerekir. Olması gereken sorumlulukların ayrı ayrı ve yeniden kullanılabilir şekilde bölünmesidir.

public class FileLoger{
    public static void WriteLog(string log) {}   
}

public class MessageSender{
    public static void Send(string message){}
}

public class UserService
{
    public void Register(string email, string name)
    {
        try
        {
            //Save User

            MessageSender.Send("Success");
        }
        catch (Exception ex)
        {
            FileLoger.WriteLog(ex.Message);
        }    
    }
}   

Bu şekilde UserService sınıfının sorumlulukları başka sınıflara devredilerek single responsibility sağlanmıştır. Ancak UserService sınıfı MessageSender ve FileLoger sınıflarına bağımlı durumdadır. Bu durum Tightly Coupled – Sıkı Bağlılık olarak adlandırılmaktadır. Bu sorunuda Interface kullanarak çözebiliriz.

public interface ILoger
{
    void Write(string Exception);
}

public interface IMessageSender
{
    void Send(string Message);
}

public class FileLoger : ILoger
{
    public void Write(string Exception)
    {
        Console.WriteLine("File Log = " +Exception);
    }
}

public class DbLoger : ILoger
{
    public void Write(string Exception)
    {
        Console.WriteLine("Db Log = " + Exception);
    }
}

public class MailSender : IMessageSender
{
    public void Send(string Message)
    {
        Console.WriteLine("Email Message = " + Message);
    }
}

public class SmsSender : IMessageSender
{
    public void Send(string Message)
    {
        Console.WriteLine("Sms Message = " + Message);
    }
}


public class UserService
{
    private ILoger _loger;
    private IMessageSender _messageSender;

    public UserService(ILoger loger, IMessageSender messageSender)
    {
        _loger = loger;
        _messageSender = messageSender;
    }

    public void Register(string email, string name)
    {
        try
        {
            //Save User

            //Send Mail
            _messageSender.Send("success");

        }
        catch (Exception ex)
        {
            //Error Log
            _loger.Write(ex.Message);
        }
    }
}

static Main (string [] args){

    var mailSender = new MailSender();
    var smsSender = new SmsSender();

    var fileLoger = new FileLoger();
    var dbLoger = new DbLoger();

    var userService = new UserService(fileLoger, mailSender);
    userService.Register("mail1@google.com","password1");

    userService = new UserService(dbLoger, smsSender);
    userService.Register("mail2@google.com", "password2");
} 

Görüleceği üzere UserService sınıfı bağımlılıklarını interface üzerinden bildiği için daha alt düzeydeki sınıflarla bağımlığı kalmamıştır. Ancak uygulamayı oluşturan bileşenlerden birinde bir değişiklik olduğunda uygulamanın tekrar derlenmesi gerekmektedir. Bu sorunun çözümünü bir sonraki makalede inceliyor olacağız

Sağlıklı ve huzurlu günler dilerim