2
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

WinFormsでKMLファイルを読み込んでPlacemarkだけ取得する

KML

KML(ケイエムエル)は、アプリケーション・プログラムにおける三次元地理空間情報の表示の管理などを目的とした情報をXMLで記述するものである。2008年4月にKML2.2版は、そのままOpen Geospatial Consortium, Inc (OGC) という地理情報システムのオープンソース化を目指す団体の規格にOGC KMLとして取り入れられた[1]。

引用:https://ja.wikipedia.org/wiki/KML

Google Earth からKMLファイルを出力

2018-08-02_15h35_11.jpg

ピンを2つたてました。(スカイツリー左右)

KMLとして出力してファイルの中身をみてみます。

スカイツリー.kml
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
    <name>スカイツリー.kml</name>
~~省略~~
    <Folder>
        <name>スカイツリー</name>
        <open>1</open>
        <Placemark>
            <name>スカイツリー左</name>
            <LookAt>
                <longitude>139.8110927515624</longitude>
                <latitude>35.71549218701607</latitude>
                <altitude>31.79836753088749</altitude>
                <heading>-0.01903135137708219</heading>
                <tilt>73.44894794879946</tilt>
                <range>1973.689702402252</range>
                <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
            </LookAt>
            <styleUrl>#m_ylw-pushpin</styleUrl>
            <Point>
                <gx:drawOrder>1</gx:drawOrder>
                <coordinates>139.8096615295515,35.70967775354634,0</coordinates>
            </Point>
        </Placemark>
        <Placemark>
            <name>スカイツリー右</name>
            <LookAt>
                <longitude>139.8110927515624</longitude>
                <latitude>35.71549218701607</latitude>
                <altitude>31.79836753088749</altitude>
                <heading>-0.01903135137708219</heading>
                <tilt>73.44894794879946</tilt>
                <range>1973.689702402252</range>
                <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
            </LookAt>
            <styleUrl>#m_ylw-pushpin</styleUrl>
            <Point>
                <gx:drawOrder>1</gx:drawOrder>
                <coordinates>139.8114844589048,35.70980495245687,0</coordinates>
            </Point>
        </Placemark>

どうやらピンの位置情報は Placemark タグに記述されてるようですね。
さらに言うとPlacemark-Pointにピンの座標が入っています。
(LookAtはスカイツリーの座標っぽいです。)

ここを取得しようというのが目的。

ライブラリを探してみる

大先輩方が既に便利なもの作ってくれているだろうと思って探してみるとこれぞC#erみたいな(意味不明
ライブラリ発見(MITライセンス)

SharpKml.Core - github
SharpKml.Core - LICENSE

SharpKml と SharpKml.Coreがありますが、.Coreのほうを使います。

インストール

Nugetでインストール

2018-08-02_11h57_00.jpg

読み込み

よくあるボタン押下→ファイル参照ダイアログでファイル指定→指定されたファイルを読み込む
っていうのをやってみます。

※Examplesがちゃんとあるのでそれ参考に作っただけですけどね!

KML読み込み部分

※ログ出力は一般的な?ロギングライブラリ NLog を使用。

KmlReader.cs
    public class KmlReader
    {
        private static readonly ILogger _log = LogManager.GetCurrentClassLogger();

        public List<Coordinate> Read( string filename )
        {
            try
            {
                using( var stream = File.Open( filename, FileMode.Open ) )
                {
                    var file = KmlFile.Load( stream );
                    var kml = file.Root as KML;
                    if( kml == null )
                        return null;

                    var placemarks = new List<Placemark>();
                    ExtractPlacemarks( kml.Feature, placemarks );
                    return placemarks.Select( ToCoordinate ).ToList();
                }
            }
            catch( Exception ex )
            {
                _log.Debug( ex, "KMLファイル読み込みでエラー" );
                return null;
            }
        }

        private void ExtractPlacemarks( Feature feature, List<Placemark> placemarks )
        {
            var placemark = feature as Placemark;
            if( placemark != null )
            {
                placemarks.Add( placemark );
            }
            else
            {
                var container = feature as Container;
                if( container != null )
                {
                    foreach( var f in container.Features )
                        ExtractPlacemarks( f, placemarks );
                }
            }
        }

        private Coordinate ToCoordinate( Placemark placemark )
        {
            var point = placemark.Geometry as Point;
            if( point == null )
                return null;

            return new Coordinate
            {
                Name = placemark.Name,
                Latitude = point.Coordinate.Latitude,
                Longitude = point.Coordinate.Longitude,
                Altitude = point.Coordinate.Altitude ?? 0
            };
        }
    }

KmlFile.Load で取得したKMLオブジェクトだとよく分からない状態なので、
SharpKml.DOM名前空間にいるそれっぽいクラスにキャスト(※)してそれっぽいやつを取り出します。
※as でやるとセーフキャスト。キャストできないときにExceptionを投げずにnullとなります。

必要な情報だけにするために
ToCoordinateメソッドでPlacemarkから名称と座標だけのCoordinateクラスに射影(変換)しています。

Coordinate.cs
    public class Coordinate
    {
        /// <summary>
        /// Placemark名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 緯度
        /// </summary>
        public double Latitude { get; set; }

        /// <summary>
        /// 経度
        /// </summary>
        public double Longitude { get; set; }

        /// <summary>
        /// 高度
        /// </summary>
        public double Altitude { get; set; }
    }

実際にファイルを指定して読み込んでみましょう。

OpenFileDialogを使用します。
毎回インスタンス生成するのはめんどくさいので自分でクラス作ってそっから呼び出します。

FileSelector.cs
    public class FileSelector
    {
        public string Select()
        {
            var dialog = new OpenFileDialog();
            dialog.FileName = "";
            dialog.Filter = "KMLファイル(*.kml)|*.kml|すべてのファイル(*.*)|*.*";
            dialog.Title = "KMLファイルを選択してください。";

            return dialog.ShowDialog() == DialogResult.Cancel
                       ? ""
                       : dialog.FileName;
        }
    }

適当にボタンのClickイベントハンドラなどから呼び出します。

    private void buttonKmlReader_Click( object sender, EventArgs e )
    {
        var fileSelector = new FileSelector();
        var path = fileSelector.Select();
        if( string.IsNullOrEmpty( path ) )
            return;

        var kmlReader = new KmlReader();
        var coordinates = kmlReader.Read( path );
        if( coordinates == null )
            return;

        foreach( var coordinate in coordinates )
        {
            Console.WriteLine( coordinate.Name );
            Console.WriteLine( $"lat: {coordinate.Latitude}, lng: {coordinate.Longitude}" );
            Console.WriteLine( "===========================" );
        }
    }

2018-08-02_15h48_12.jpg

多少誤差が出ますがちゃんと取れてますね。

おしまい

この他のKMLファイル操作はSharpKmlのgithubページのExamplesみながらやってみて下さい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
2
Help us understand the problem. What are the problem?