Openlayers 3でPostgis GeoJSONを使用する簡単な方法はありますか


8

Jason Sanford(https://github.com/JasonSanford)のgoogle-vector-layersとleaflet-vector-layers を使用して、Postgis-databasesからのデータのカスタマイズされたポップアップを簡単に表示、スタイル設定、および追加しました。これは、Bryan McBrideのPHP-Database-GeoJSONの修正バージョンと組み合わせて機能します。

Openlayers 3での使用に匹敵するものはありませんか?私はそのようなライブラリを書くためのプログラミングスキルを持っていないことを認めなければなりません。誰かが同等のコードについて知っているかもしれません。多くのグーグル操作を行った後、特定の問題に対する多くの答えを見つけ、AjaxとboundingBox戦略を使用してGeoJSONをロードする方法、ベクターレイヤーのスタイルを設定してポップアップを追加する方法を実装しましたが、まだ簡単な方法がありませんこれらすべてを組み合わせる方法の方法。

PostGis> GeoJSON> Openlayers 3(スタイリングとポップアップによる機能の表示を含む)はかなり標準化された方法であると思うので、今日まで見つからなかった既存のソリューションがあるのではないかと思います。


gis.stackexchange.com/questions/134688/…。ベクターレイヤーのsrcの一部としてURLを追加します(もちろん、スタイルを設定できます)。
John Powell、

私はすでにこの投稿を研究しましたが、前に述べたように、vector-layers-scriptsのようなオールインワンのソリューションはありません。
geom 2016年

githubページへのリンクからは一目瞭然ではないため、vector-layers-scriptsがどのように機能するかを示す例と、その点でOL3の例に欠けている例を追加することをお勧めします。
John Powell

回答:


7

私の質問に対する答えがないため、他の既存のコードからインスピレーションを得ようとし、openlayers 3の基本的なGeoJSONコンストラクターの開発を続けました。

私は実際にできます

  • テーブル名、テーブルフィールド、およびWHERE句を渡すことにより、PostGISからさまざまなレイヤーをロードします(PosgreSQL / PostGisのすべての可能なマジックを使用)
  • スタイルを設定する(単一の値、一意の値、または範囲の値を使用)
  • 最小/最大解像度でレイヤーを表示/非表示
  • 機能が複数回読み込まれるのを防ぐために、一意の識別子をidにする必要があります
  • レイヤーはol.loadingstrategy.bbox戦略を介して読み込まれるため、可視データのみが読み込まれます
  • レイヤー固有のポップアップテンプレートを定義する
  • レイヤー固有のラベルを追加する

ですから、それはすでに面白いパッケージであり、非常に簡単に適応(および改善)することができます。したがって、ここでコードを共有したいと思います。全体は3つの部分で構成されます。

  • ol3Vector.js ol.layer.Vectorの拡張クラス
  • マップとすべてのレイヤーが定義されているmap.jsファイル
  • sql-stringを構築し、有効なGeoJSONを返すためにサーバーサイドで使用されるget_geojson.phpファイル。

正しく機能させるには、ol3Vector.jsを最初にロードする必要があります。これがコードです:

 // class to load vector layers from Postgis using a modified php-script from
 // Bryan McBride
 // https://github.com/bmcbride/PHP-Database-GeoJSON 
 //
 ol3Vector = function(options) {

 //
 // -------- Defining default style settings ----------
 //
 var fill = new ol.style.Fill({
    color: 'rgba(255,255,255,0.4)'
 });
 var stroke = new ol.style.Stroke({
    color: '#3399CC',
    width: 1.25
 });
 var text = new ol.style.Text({
    text: "",
    font: "16px Calibri,sans-serif",
    fill: new ol.style.Fill({
        color: [255, 255, 255, 1]
    }),
    stroke: new ol.style.Stroke({
        color: [0, 0, 0, 1],
        width: 2.5
    })
});

var image = new ol.style.Circle({  // actually point styling only works with image, and not with icons
    fill: fill,
    stroke: stroke,
    radius: 5
});

var style = new ol.style.Style({
    image: image,
    fill: fill,
    stroke: stroke,
    text: text
});

var styles = [style];

//
// ------------- defining options to build the new ol3Vector-layer -----------
//
var options = {
    title: options.title,
    visible: false,
    geotable: options.geotable,  // table name in PostGis-database
    fields: options.fields,      // field-names
    where: options.where,        // where-string passed to PostGis
    source: new ol.source.Vector({
        projection: "EPSG:4326",
        attributions: [new ol.Attribution({
            html: options.attribution
        })],
        strategy: ol.loadingstrategy.bbox,   //load only data off the visible map
        loader: function(extent, resolution, projection) {
            var extent = ol.proj.transformExtent(extent, projection.getCode(), ol.proj.get('EPSG:4326').getCode());
            $.ajax({
                type: "GET",
                dataType: "json",
                url: "./mapdata/get_geojson.php?" +     // define path to the get_geojson.php script
                    "geotable=" + options.geotable +
                    "&fields=" + options.fields +
                    "&where=" + options.where +
                    "&bbox=" + extent.join(","),
                context: this
            }).done(function(data) {
                var format = new ol.format.GeoJSON();
                this.addFeatures(format.readFeatures(data, {
                    dataProjection: "EPSG:4326",
                    featureProjection: "EPSG:3857"
                }));

            });

        }
    }),
    minResolution: options.minResolution,
    maxResolution: options.maxResolution,
    content: options.content,
    symbology: options.symbology,
    showLabels: options.showLabels,
    label: options.label,
    style: (function label_style() { // style function needed to be wrapped in a function to get it work
        // ------ labeling -----------
        var layerLabel = "";
        if (!options.showLabels) {
            layerLabel = "";
        } else if (options.showLabels) {
            layerLabel = options.label;
        }
        return function(feature, resolution) {
            style.getText().setText(feature.get(layerLabel));

            // ------ styling -------------
            if (!options.symbology) {
                return style;
                //            return false;        
            } else if (options.symbology) {

                var atts = feature.getProperties();

                switch (options.symbology.type) {
                    case "single":
                        // Its a single symbology for all features so just set the values for fill and stroke
                        //
                        switch (feature.getGeometry().getType()) {
                            case "Point":
                            case "MultiPoint":

                                style.image = style.getImage().getFill().setColor(options.symbology.styleOptions.fill);
                                style.image = style.getImage().getStroke().setColor(options.symbology.styleOptions.color);
                                style.image = style.getImage().getStroke().setWidth(options.symbology.styleOptions.width);
                                style.image = style.getImage().setRadius(options.symbology.styleOptions.radius);
                                break;

                            case "LineString":
                            case "MultiLineString":
                            case "Polygon":
                            case "MultiPolygon":

                                style.fill = style.setFill(new ol.style.Fill({
                                    color: options.symbology.styleOptions.fill
                                }));
                                style.stroke = style.setStroke(new ol.style.Stroke({
                                    color: options.symbology.styleOptions.color,
                                    width: options.symbology.styleOptions.width
                                }));
                                break;
                        }
                        break;

                    case "unique":
                        // Its a unique symbology. Check if the features property value matches that in the symbology and style accordingly
                        // 
                        var att = options.symbology.property;
                        for (var i = 0, len = options.symbology.values.length; i < len; i++) { // field with values to define styles
                            if (atts[att] == options.symbology.values[i].value) { // unique value to identify style
                                for (var key in options.symbology.values[i].styleOptions) {

                                    switch (feature.getGeometry().getType()) {
                                        case "Point":
                                        case "MultiPoint":

                                            style.image = style.getImage().getFill().setColor(options.symbology.values[i].styleOptions.fill);
                                            style.image = style.getImage().getStroke().setColor(options.symbology.values[i].styleOptions.color);
                                            style.image = style.getImage().getStroke().setWidth(options.symbology.values[i].styleOptions.width);
                                            style.image = style.getImage().setRadius(options.symbology.values[i].styleOptions.radius);
                                            break;

                                        case "LineString":
                                        case "MultiLineString":
                                        case "Polygon":
                                        case "MultiPolygon":

                                            style.fill = style.setFill(new ol.style.Fill({
                                                color: options.symbology.values[i].styleOptions.fill
                                            }));
                                            style.stroke = style.setStroke(new ol.style.Stroke({
                                                color: options.symbology.values[i].styleOptions.color,
                                                width: options.symbology.values[i].styleOptions.width
                                            }));
                                            break;
                                    }
                                }
                            }
                        }
                        break;

                    case "range":
                        // Its a range symbology. Check if the features property value is in the range set in the symbology and style accordingly
                        //
                        var att = options.symbology.property;
                        for (var i = 0, len = options.symbology.ranges.length; i < len; i++) {
                            if (atts[att] >= options.symbology.ranges[i].range[0] && atts[att] <= options.symbology.ranges[i].range[1]) {
                                for (var key in options.symbology.ranges[i].styleOptions) {

                                    switch (feature.getGeometry().getType()) {
                                        case "Point":
                                        case "MultiPoint":

                                            style.image = style.getImage().getFill().setColor(options.symbology.ranges[i].styleOptions.fill);
                                            style.image = style.getImage().getStroke().setColor(options.symbology.ranges[i].styleOptions.color);
                                            style.image = style.getImage().getStroke().setWidth(options.symbology.ranges[i].styleOptions.width);
                                            style.image = style.getImage().setRadius(options.symbology.ranges[i].styleOptions.radius);
                                            break;

                                        case "LineString":
                                        case "MultiLineString":
                                        case "Polygon":
                                        case "MultiPolygon":

                                            style.fill = style.setFill(new ol.style.Fill({
                                                color: options.symbology.ranges[i].styleOptions.fill
                                            }));
                                            style.stroke = style.setStroke(new ol.style.Stroke({
                                                color: options.symbology.ranges[i].styleOptions.color,
                                                width: options.symbology.ranges[i].styleOptions.width
                                            }));
                                            break;
                                    }
                                }
                            }
                        }

                        break;
                }
            }
            return styles;
        }
    })()
}

ol.layer.Vector.call(this, options);

};

ol.inherits(ol3Vector, ol.layer.Vector);

ここにmap.jsの例

function init() {
document.removeEventListener('DOMContentLoaded', init);

//
// ------- Layers and Map ----------------
//
var baselayers = new ol.layer.Group({  
    title: 'Baselayers',
    layers: [
        new ol.layer.Tile({
            title: 'OSM',
            type: 'base',
            visible: true,
            source: new ol.source.OSM()
        })
    ]
});

var toplayers = new ol.layer.Group({
    title: 'Toplayer',
    layers: []
});


var view = new ol.View({
    center: ol.proj.fromLonLat([6.2, 49.6]),
    zoom: 12
});

var popup_div = document.getElementById('popup');
var popup = new ol.Overlay({
    element: popup_div,
    positioning: 'bottom-center',
    stopEvent: false
});

var map = new ol.Map({
    target: 'map',
    view: view,
    controls: ol.control.defaults().extend([
        new ol.control.Zoom(),
        new ol.control.FullScreen(),
        new ol.control.ZoomSlider(),
        new ol.control.LayerSwitcher({  //layergroups are used with the layerswitcher from https://github.com/walkermatt/ol3-layerswitcher
            tipLabel: 'Layer Switcher'
        }),
        new ol.control.OverviewMap(),
        new ol.control.ScaleLine()
    ]),
    overlays: [popup],
    layers: [baselayers, toplayers],
    interactions: ol.interaction.defaults().extend([
        new ol.interaction.Select({
            layers: [baselayers, toplayers]
        })
    ])
});

//
// ------------ define toplayers -------------------
//
var n2k_dh_l = new ol3Vector({
    title: "Natura 2000 Habitats Directive",   // name of the layer to show up in the layerswitcher
    attribution: "<br />Réseau Natura 2000 Habitats Directive",
    geotable: "n2k_dh",
    fields: "gid as id, sitecode, sitename, surfha",
    where: "sitename ilike '%moselle%'",    // You can use all the PostgreSQL or PostGis features here
    symbology: {
        type: "single",
        styleOptions: {
            fill: "rgba(100,250,0,0.1)",   // define colors as rgba()
            color: "green",                // or simple color-names
            width: 2
        }
    },
    minResolution: 0.01,
    maxResolution: 50,
    content: "<p><strong> BH {sitecode}</strong><hr>{sitename}<br />{surfha} ha </p>",
    showLabels: true,    // show labels on map
    label: "sitename"    // field used for labeling
});

var n2k_do_l = new ol3Vector({
    title: "Natura 2000 Birds Directive",
    attribution: "<br />Réseau Natura 2000 Birds Directive",
    geotable: "n2k_do",
    fields: "gid as id, sitecode, sitename, surfha",
    where: "",
    symbology: {
        type: "single",
        styleOptions: {
            fill: "rgba(100,250,0,0.1)",
            color: "magenta",
            width: 2
        }
    },
    minResolution: 0.01,
    maxResolution: 50,
    content: "<p><strong> BD {sitecode}</strong><hr>{sitename}<br />{surfha} ha </p>",
    showLabels: true,
    label: "sitecode"
});

var communes = new ol3Vector({
    map: map,
    title: "Communes",
    attribution: "<br />Communes",
    geotable: "communes",
    fields: "gid as id, surfha, commune, stats",
    where: "",
    symbology: {
        type: "unique",
        property: "stats",  // field used to style depending on their value
        values: [{
                value: "AB", 
                styleOptions: {
                    fill: "rgba(0,0,0,0.0)",
                    color: "grey",
                    width: 1.25
                }
            },
            {
                value: "CD",
                styleOptions: {
                    fill: "rgba(100,250,0,0.1)",
                    color: "green",
                    width: 2
                }
            },
            {
                value: "",
                styleOptions: {
                    fill: "rgba(250,0,0,0.1)",
                    color: "red",
                    width: 2
                }
            }
        ]
    },
    minResolution: 0.01,
    maxResolution: 50,
    content: "<p><strong>{commune}</strong><hr>{stat}<br />{surfha} ha </p>",
    showLabels: true,   
    label: "commune"    
});

var n2k_dh_r = new ol3Vector({
    map: map,
    title: "Natura 2000 Habitats Directive - site size",
    attribution: "<br />Réseau Natura 2000 Habitats Directive",
    geotable: "n2k_dh",
    fields: "gid as id, sitecode, sitename, surfha",
    where: "",
    symbology: {
        type: "range",
        property: "surfha", // field that holds values that should be displayed as ranges
        ranges: [{
            range: [1, 100], // defining range min and max values of property field
            styleOptions: {
                fill: "rgba(220,20,60,0.3)",
                color: "crimson",
                width: 1
            }
        }, {
            range: [101, 1000],
            styleOptions: {
                fill: "rgba(255,165,0,0.3)",
                color: "orange",
                width: 1
            }
        }, {
            range: [1001, 10000],
            styleOptions: {
                fill: "rgba(255,255,0,0.3)",
                color: "Yellow",
                width: 1
            }
        }]
    },
    minResolution: 0.01,
    maxResolution: 50,
    content: "<p><strong> BH {sitecode}</strong><hr>{sitename}<br />{surfha} ha </p>",
    showLabels: true,
    label: "sitecode"
});

var btk_p = new ol3Vector({
    map: map,
    title: "Habitats points",  // point layers
    attribution: "<br />Cartho",
    geotable: "btk_p",
    fields: "gid as id, btyp1_code, btyp1_name, cs",
    where: "",
    symbology: {
        type: "unique",
        property: "cs",
        values: [{
                value: "A", 
                styleOptions: {
                    fill: "rgba(0,128,0,0.3)",
                    color: "rgba(0,128,0,0.3)",
                    width: 1,
                    radius: 20 // must be set in order to render point features    
                }
            },
            {
                value: "B",
                styleOptions: {
                    fill: "rgba(255,165,0,0.3)",
                    color: "rgba(255,165,0,0.3)",
                    width: 1,
                    radius: 15
                }
            },
            {
                value: "C",
                styleOptions: {
                    fill: "rgba(255,0,0,0.3)",
                    color: "rgba(255,0,0,0.3)",
                    width: 1,
                    radius: 10
                }
            }
        ]
    },
    minResolution: 0.01,
    maxResolution: 50,
    content: "<p><strong>{btyp1_code}</strong><hr>{btyp1_name}<br />{cs}</p>",
    showLabels: false,
    label: "bewertung1"
});

// ------------------ add ol3Vectors to toplayers ----------
// 
toplayers.setLayers(new ol.Collection([n2k_do_l, n2k_dh_l, communes, n2k_dh_r, btk_p]));
//
// ------------------ show popups based on content-template for different layers --------------------
//  
map.on('click', function(evt) {
    var feature = map.forEachFeatureAtPixel(evt.pixel,
        function(feature, layer) {
            return feature;
        });
    var popupContent = map.forEachLayerAtPixel(evt.pixel,
        function(layer) {
            return layer.get('content');
        });
    if (feature) {
        popup.setPosition(evt.coordinate);
        var atts = feature.getProperties();
        for (var prop in atts) {
            var re = new RegExp("{" + prop + "}", "g");
            popupContent = popupContent.replace(re, atts[prop]);
        }
        $(popup_div).attr('data-placement', 'auto');
        $(popup_div).attr('data-content', popupContent);
        $(popup_div).attr('data-html', true);
        $(popup_div).popover();
        $(popup_div).popover('show');
        $('.popover-title').click(function() {
            $(popup_div).popover('destroy');
        });
    } else {
        $(popup_div).popover('destroy');
    }
});
} // End function init()
document.addEventListener('DOMContentLoaded', init);

最後に、PostGis-databaseからデータをフェッチするためにget_geojson.phpが必要です。

<?php
/**
 * GET GeoJSON from PostGIS
 * Query a PostGIS table or view and return the results in GeoJSON format, suitable for use in OpenLayers, Leaflet, etc.
 * Author:  Bryan R. McBride, GISP, adapted by G.Moes
 * Contact: bryanmcbride.com
 * GitHub:  https://github.com/bmcbride/PHP-Database-GeoJSON
 * 
 * @param       string      $geotable       The PostGIS layer name *REQUIRED*
 * @param       string      $geomfield      The PostGIS geometry field *REQUIRED*
 * @param       string      $srid           The SRID of the returned GeoJSON *OPTIONAL (If omitted, EPSG: 2169 will be used)*
 * @param       string      $fields         Fields to be returned *OPTIONAL (If omitted, all fields will be returned)* 
 *                              NOTE- Uppercase field names should be wrapped in double quotes
 * @param       string      $parameters     SQL WHERE clause parameters *OPTIONAL*
 * @param       string      $orderby        SQL ORDER BY constraint *OPTIONAL*
 * @param       string      $sort           SQL ORDER BY sort order (ASC or DESC) *OPTIONAL*
 * @param       string      $limit          Limit number of results returned *OPTIONAL*
 * @param       integer     $precision      digits of returned geojson 6 = 0.111 m submeter as DEFAULT *OPTIONAL*
 * @param       real        $simplify       simplify geometry to >5.0m as DEFAULT *OPTIONAL*  
 * @param       string      $offset         Offset used in conjunction with limit *OPTIONAL*
 * @return      string                  resulting geojson string
 */


# Connect to PostgreSQL database You need to pass here the credentials to connect to Your database
require("../database/connect.php");


function escapeJsonString($value) { # list from www.json.org: (\b backspace, \f formfeed)
  $escapers = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c");
  $replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b");
  $result = str_replace($escapers, $replacements, $value);
  return $result;
}


# Retrive URL variables
if (empty($_GET['geotable'])) {
    echo "missing required parameter: <i>geotable</i>";
    exit;
} else
    $geotable = $_GET['geotable'];
if (empty($_GET['geomfield'])) {
    $geomfield='the_geom';
} else
    $geomfield = $_GET['geomfield'];
if (empty($_GET['srid'])) {
    $srid = 2169;    // changethis if You need another standard SRID
} else
    $srid = $_GET['srid'];
if (empty($_GET['fields'])) {
    $fields = '*';
} else
    $fields = $_GET['fields'];
$parameters = $_GET['where'];
$bbox = $_GET['bbox'];
if (empty($_GET['precision'])) {
    $precision = 6;    // change this to Your needs
} else
    $precision = $_GET['precision'];
if (empty($_GET['simplify'])) {
    $simplify = 5.0;   // change this to Your needs
} else
    $simplify = $_GET['simplify'];    
$orderby    = $_GET['orderby'];
if (empty($_GET['sort'])) {
    $sort = 'ASC';
} else
    $sort = $_GET['sort'];
$limit      = $_GET['limit'];
$offset     = $_GET['offset'];



# Build SQL SELECT statement and return the geometry as a GeoJSON element in EPSG: 4326
$sql = "SELECT " . pg_escape_string($fields) . ", st_asgeojson(st_transform(ST_SimplifyPreserveTopology(" . pg_escape_string($geomfield) . ",".$simplify."),4326),".$precision.") AS geojson FROM " . pg_escape_string($geotable);
if (strlen(trim($parameters)) > 0) {

      $sql .= " WHERE " . str_replace("''", "'", pg_escape_string($parameters));
}
if (strlen(trim($parameters)) > 0 AND strlen(trim($bbox)) > 0){
     $sql .= " AND the_geom &&  st_transform(st_makeenvelope(".pg_escape_string ($bbox).",4326),".$srid.")";
}
if (strlen(trim($parameters)) <= 0 AND strlen(trim($bbox)) > 0) {
      $sql .= " WHERE the_geom &&  st_transform(st_makeenvelope(".pg_escape_string ($bbox).",4326),".$srid.")";
}
if (strlen(trim($orderby)) > 0) {
    $sql .= " ORDER BY " . pg_escape_string($orderby) . " " . $sort;
}
if (strlen(trim($limit)) > 0) {
    $sql .= " LIMIT " . pg_escape_string($limit);
}
if (strlen(trim($offset)) > 0) {
    $sql .= " OFFSET " . pg_escape_string($offset);
}
//echo $sql;
# Try query or error
$rs = pg_query($db_handle, $sql);
if (!$rs) {
    echo "An SQL error occured.\n";
    echo $sql;
    exit;
}
# Build GeoJSON
$output    = '';
$rowOutput = '';
while ($row = pg_fetch_assoc($rs)) {
    $rowOutput = (strlen($rowOutput) > 0 ? ',' : '') . '{"type": "Feature", "geometry": ' . $row['geojson'] . ', "properties": {';
    $props = '';
    $id    = '';
    foreach ($row as $key => $val) {
        if ($key != "geojson") {
            $props .= (strlen($props) > 0 ? ',' : '') . '"' . $key . '":"' . escapeJsonString($val) . '"';
        }
        if ($key == "id") {
            $id .= ',"id":"' . escapeJsonString($val) . '"';
        }
    }

    $rowOutput .= $props . '}';
    $rowOutput .= $id;
    $rowOutput .= '}';
    $output .= $rowOutput;
}
$output = '{"type": "FeatureCollection", "features": [ ' . $output . ' ]}';

header('Content-type:application/json;charset=utf-8');
echo json_encode( $output);
?>

楽しんで!これが誰かがこのコードをさらに進めたり改善したりするのを助け、私がGitHubで公開できるようにしてください。


パフォーマンスを大幅に改善するPOSTGIS関数はありますか?私は一度に50k ish機能について話している
Revo

多分:新しいol.source.ImageVector()?
Revo

1
これらのスクリプト(JSおよびPHP)は、注入
JGH

実際、これらのJSおよびPHPスクリプトは、レイヤーをマップにロードするために内部的にのみ呼び出されます。phpスクリプトは、ログインしている場合にのみ実行されます。ログインしている人なら誰でも、GET文字列を簡単に操作して悪意のあるSQLを実行できます。リンクを調べて、注射を防ぐ方法を確認します。コードを見ていただきありがとうございます。
geom 2017

1
ホイールを再発明するような感じがしますが、WFSを使用するだけではどうですか。
nmtoken
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.