From 7fd251fa63e287bf5e2be0748e7172e475d59330 Mon Sep 17 00:00:00 2001
From: sebastianpauli <>
Date: Fri, 26 Aug 2022 17:56:12 +0200
Subject: [PATCH] Quick first implementation of the new SearchParcel module.
---
demo/editor.html | 12 +-
demo/lanis.html | 213 ++++++++++++++++++
demo/proxy.php | 24 ++
src/netgis/Client.css | 2 +-
src/netgis/Client.js | 8 +-
src/netgis/Events.js | 5 +
src/netgis/LayerTree.css | 5 +
src/netgis/LayerTree.js | 14 ++
src/netgis/MapOpenLayers.js | 66 ++++++
src/netgis/Menu.js | 9 +
src/netgis/Modes.js | 3 +-
src/netgis/SearchParcel.css | 131 +++++++++++
src/netgis/SearchParcel.js | 431 ++++++++++++++++++++++++++++++++++++
src/netgis/Theme.css | 1 +
14 files changed, 918 insertions(+), 6 deletions(-)
create mode 100644 demo/lanis.html
create mode 100644 demo/proxy.php
create mode 100644 src/netgis/SearchParcel.css
create mode 100644 src/netgis/SearchParcel.js
diff --git a/demo/editor.html b/demo/editor.html
index 289b8bf..eed1870 100644
--- a/demo/editor.html
+++ b/demo/editor.html
@@ -7,7 +7,7 @@
-
NetGIS Client
+ Editor Demo | NetGIS Client
@@ -19,6 +19,7 @@
+
@@ -71,6 +72,7 @@
+
@@ -79,7 +81,13 @@
diff --git a/demo/lanis.html b/demo/lanis.html
new file mode 100644
index 0000000..a91aa0e
--- /dev/null
+++ b/demo/lanis.html
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+ LANIS Demo | NetGIS Client
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/proxy.php b/demo/proxy.php
new file mode 100644
index 0000000..cc27e44
--- /dev/null
+++ b/demo/proxy.php
@@ -0,0 +1,24 @@
+', true );
+ searchParcel.addEventListener( "click", this.onSearchParcelClick.bind( this ) );
+ wrapper.appendChild( searchParcel );
+
/*var title = this.createButton( 'GeoPortal', false );
title.classList.remove( "netgis-hover-primary" ); //TODO: createText function?
//title.style.padding = "0mm";
@@ -189,6 +193,11 @@ netgis.Menu.prototype.onSearchPlaceClick = function( e )
this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.SEARCH_PLACE );
};
+netgis.Menu.prototype.onSearchParcelClick = function( e )
+{
+ this.client.invoke( netgis.Events.SET_MODE, netgis.Modes.SEARCH_PARCEL );
+};
+
netgis.Menu.prototype.onSearchDataClick = function( e )
{
alert( "TODO: data search interface" );
diff --git a/src/netgis/Modes.js b/src/netgis/Modes.js
index 13bae62..b339d0b 100644
--- a/src/netgis/Modes.js
+++ b/src/netgis/Modes.js
@@ -21,6 +21,7 @@ netgis.Modes = Object.freeze
BUFFER_FEATURE_BEGIN: "BUFFER_FEATURE_BEGIN",
BUFFER_FEATURE_EDIT: "BUFFER_FEATURE_EDIT",
- SEARCH_PLACE: "SEARCH_PLACE"
+ SEARCH_PLACE: "SEARCH_PLACE",
+ SEARCH_PARCEL: "SEARCH_PARCEL"
}
);
\ No newline at end of file
diff --git a/src/netgis/SearchParcel.css b/src/netgis/SearchParcel.css
new file mode 100644
index 0000000..a5e4c9e
--- /dev/null
+++ b/src/netgis/SearchParcel.css
@@ -0,0 +1,131 @@
+
+/* TODO: refactor into common panel class */
+
+.netgis-search-parcel
+{
+ position: absolute;
+ right: 0mm;
+ width: 100%;
+ max-width: 100mm;
+ top: 12mm;
+ bottom: 0mm;
+ overflow: auto;
+ z-index: 200;
+
+ padding: 4mm;
+
+ -webkit-transform: none;
+ transform: none;
+ transition: transform 150ms ease;
+}
+
+.netgis-search-parcel.netgis-hide
+{
+ display: initial;
+
+ -webkit-transform: translateX( 110% );
+ transform: translateX( 110% );
+ transition: transform 150ms ease;
+ will-change: transform;
+}
+
+.netgis-search-parcel h3
+{
+ margin: 0mm;
+ margin-bottom: 4mm;
+}
+
+.netgis-search-parcel label
+{
+ display: block;
+ margin: 3mm 0mm;
+ margin-bottom: 0mm;
+ cursor: pointer;
+}
+
+.netgis-search-parcel input
+{
+ width: 100%;
+ height: 12mm;
+ margin: 3mm 0mm;
+ padding: 0mm 3mm;
+}
+
+.netgis-search-parcel .netgis-loader
+{
+ width: 6mm;
+ height: 6mm;
+ top: 8mm;
+ right: 6mm;
+ font-size: 6mm;
+}
+
+.netgis-search-parcel ul
+{
+ margin: 0mm;
+ padding: 0mm;
+ list-style-type: none;
+}
+
+.netgis-search-parcel li button
+{
+ text-align: left;
+}
+
+.netgis-search-parcel button
+{
+ display: block;
+ width: 100%;
+ height: 12mm;
+ padding: 0mm 3mm;
+ margin: 0mm;
+}
+
+.netgis-search-parcel .netgis-table-wrapper
+{
+ width: 100%;
+ /*height: 144mm; /* 12mm * 12 */
+ max-height: 120mm;
+ margin-top: 4mm;
+ overflow: auto;
+}
+
+.netgis-search-parcel table
+{
+ border-collapse: collapse;
+ white-space: nowrap;
+}
+
+.netgis-search-parcel tr
+{
+ height: 12mm;
+}
+
+.netgis-search-parcel th, .netgis-search-parcel td
+{
+ text-align: left;
+ padding: 0mm 3mm;
+}
+
+.netgis-search-parcel td
+{
+ cursor: pointer;
+}
+
+.netgis-search-parcel td:first-child
+{
+ padding: 0mm;
+}
+
+.netgis-search-parcel table button
+{
+ display: inline-block;
+ width: 12mm;
+ height: 12mm;
+}
+
+.netgis-search-parcel p
+{
+ margin: 4mm 3mm;
+ font-style: italic;
+}
\ No newline at end of file
diff --git a/src/netgis/SearchParcel.js b/src/netgis/SearchParcel.js
new file mode 100644
index 0000000..52c147f
--- /dev/null
+++ b/src/netgis/SearchParcel.js
@@ -0,0 +1,431 @@
+"use strict";
+
+var netgis = netgis || {};
+
+netgis.SearchParcel = function()
+{
+ this.client = null;
+};
+
+netgis.SearchParcel.prototype.load = function()
+{
+ this.root = document.createElement( "section" );
+ this.root.className = "netgis-search-parcel netgis-dialog netgis-shadow netgis-hide";
+
+ // Head
+ var head = document.createElement( "h3" );
+ head.innerHTML = "Flurstücks-Suche:";
+ this.root.appendChild( head );
+
+ // Form
+ var form = document.createElement( "div" );
+ this.root.appendChild( form );
+
+ // Name Input ( "Gemarkung" )
+ var nameLabel = this.createInput( "Gemarkungsname:" );
+ nameLabel.style.position = "relative";
+ form.appendChild( nameLabel );
+
+ this.nameInput = nameLabel.children[ 0 ];
+ this.nameInput.setAttribute( "title", "ENTER: Auswählen, ESCAPE: Zurücksetzen" );
+ this.nameInput.addEventListener( "keyup", this.onInputNameKey.bind( this ) );
+
+ this.nameLoader = document.createElement( "div" );
+ this.nameLoader.className = "netgis-loader netgis-text-primary netgis-hide";
+ this.nameLoader.innerHTML = "";
+ nameLabel.appendChild( this.nameLoader );
+
+ // Name Results
+ this.nameList = document.createElement( "ul" );
+ form.appendChild( this.nameList );
+
+ // District Input ( "Gemarkung" )
+ var districtLabel = this.createInput( "Gemarkungsnummer:" );
+ this.districtInput = districtLabel.children[ 0 ];
+ form.appendChild( districtLabel );
+
+ // Field Input ( "Flur" )
+ var fieldLabel = this.createInput( "Flurnummer:" );
+ this.fieldInput = fieldLabel.children[ 0 ];
+ form.appendChild( fieldLabel );
+
+ // Parcel Input ( "Flurstück" )
+ var parcelLabel = this.createInput( "Flurstücksnummer (Zähler/Nenner):" );
+ this.parcelInputA = parcelLabel.children[ 0 ];
+ this.parcelInputA.style.width = "48%";
+ this.parcelInputB = this.parcelInputA.cloneNode( true );
+ this.parcelInputB.style.marginLeft = "4%";
+ parcelLabel.appendChild( this.parcelInputB );
+ form.appendChild( parcelLabel );
+
+ // Parcel Search
+ var parcelButton = document.createElement( "button" );
+ parcelButton.setAttribute( "type", "button" );
+ parcelButton.addEventListener( "click", this.onParcelSearchClick.bind( this ) );
+ parcelButton.className = "netgis-primary netgis-hover-primary";
+ parcelButton.innerHTML = "Flurstücke suchen";
+ parcelButton.style.marginTop = "4mm";
+ form.appendChild( parcelButton );
+
+ // Parcel Results
+ this.parcelInfo = document.createElement( "p" );
+ form.appendChild( this.parcelInfo );
+
+ this.parcelTable = this.createTable( [ "", "Flur", "FS Zähler", "FS Nenner", "FKZ", "Fläche (qm)" ] );
+ this.parcelTable.classList.add( "netgis-hide" );
+ form.appendChild( this.parcelTable );
+
+ this.parcelList = this.parcelTable.getElementsByTagName( "tbody" )[ 0 ];
+
+ this.parcelReset = document.createElement( "button" );
+ this.parcelReset.setAttribute( "type", "button" );
+ this.parcelReset.addEventListener( "click", this.onParcelResetClick.bind( this ) );
+ this.parcelReset.className = "netgis-primary netgis-hover-primary";
+ this.parcelReset.innerHTML = "Zurücksetzen";
+ this.parcelReset.style.marginTop = "4mm";
+ form.appendChild( this.parcelReset );
+
+ // Initial State
+ this.reset();
+
+ // Test
+ this.requestParcel( "072856", "1" );
+
+ // Attach To Client
+ this.client.root.appendChild( this.root );
+
+ this.client.on( netgis.Events.SET_MODE, this.onSetMode.bind( this ) );
+ this.client.on( netgis.Events.LAYER_LIST_TOGGLE, this.onLayerListToggle.bind( this ) );
+};
+
+netgis.SearchParcel.prototype.createInput = function( title )
+{
+ var label = document.createElement( "label" );
+ label.className = "netgis-hover-text-primary";
+ label.innerHTML = title;
+
+ var input = document.createElement( "input" );
+ input.setAttribute( "type", "text" );
+ label.appendChild( input );
+
+ return label;
+};
+
+netgis.SearchParcel.prototype.createNameItem = function( title )
+{
+ var li = document.createElement( "li" );
+
+ var button = document.createElement( "button" );
+ button.setAttribute( "type", "button" );
+ button.addEventListener( "click", this.onNameItemClick.bind( this ) );
+ button.className = "netgis-text-primary netgis-hover-light";
+ button.innerHTML = title;
+ li.appendChild( button );
+
+ return li;
+};
+
+netgis.SearchParcel.prototype.createTable = function( columnNames )
+{
+ var wrapper = document.createElement( "div" );
+ wrapper.className = "netgis-table-wrapper";
+
+ var table = document.createElement( "table" );
+ wrapper.appendChild( table );
+
+ // Head
+ var head = document.createElement( "thead" );
+ table.appendChild( head );
+
+ var row = document.createElement( "tr" );
+ row.className = "netgis-light";
+ row.style.position = "sticky";
+ head.appendChild( row );
+
+ for ( var i = 0; i < columnNames.length; i++ )
+ {
+ var th = document.createElement( "th" );
+ th.innerHTML = columnNames[ i ];
+ row.appendChild( th );
+ }
+
+ // Body
+ var body = document.createElement( "tbody" );
+ table.appendChild( body );
+
+ return wrapper;
+};
+
+netgis.SearchParcel.prototype.createParcelItem = function( field, parcelA, parcelB, id, area, bbox, geom )
+{
+ //TODO: store geometry data on element vs. seperate data array ?
+
+ var tr = document.createElement( "tr" );
+ tr.className = "netgis-hover-light netgis-hover-text-primary";
+ tr.setAttribute( "title", "Klicken zum zoomen" );
+ tr.setAttribute( "data-bbox", bbox );
+ tr.setAttribute( "data-geom", geom );
+ tr.addEventListener( "mouseenter", this.onParcelEnter.bind( this ) );
+ tr.addEventListener( "mouseleave", this.onParcelLeave.bind( this ) );
+ tr.addEventListener( "click", this.onParcelClick.bind( this ) );
+
+ var buttonCell = document.createElement( "td" );
+ tr.appendChild( buttonCell );
+
+ var importButton = document.createElement( "button" );
+ importButton.setAttribute( "type", "button" );
+ importButton.setAttribute( "title", "Geometrie übernehmen" );
+ importButton.addEventListener( "click", this.onParcelImportClick.bind( this ) );
+ importButton.className = "netgis-text-primary netgis-hover-primary";
+ importButton.innerHTML = "";
+ buttonCell.appendChild( importButton );
+
+ var fieldCell = document.createElement( "td" );
+ fieldCell.innerHTML = field;
+ tr.appendChild( fieldCell );
+
+ var parcelCellA = document.createElement( "td" );
+ parcelCellA.innerHTML = parcelA;
+ tr.appendChild( parcelCellA );
+
+ var parcelCellB = document.createElement( "td" );
+ parcelCellB.innerHTML = parcelB;
+ tr.appendChild( parcelCellB );
+
+ var idCell = document.createElement( "td" );
+ idCell.innerHTML = id;
+ tr.appendChild( idCell );
+
+ var areaCell = document.createElement( "td" );
+ areaCell.innerHTML = area;
+ tr.appendChild( areaCell );
+
+ return tr;
+};
+
+netgis.SearchParcel.prototype.reset = function()
+{
+ this.nameLoader.classList.add( "netgis-hide" );
+
+ this.nameInput.value = "";
+ this.districtInput.value = "";
+ this.fieldInput.value = "";
+ this.parcelInputA.value = "";
+ this.parcelInputB.value = "";
+
+ this.nameList.innerHTML = "";
+ this.parcelInfo.innerHTML = "";
+ this.parcelList.innerHTML = "";
+ this.parcelTable.classList.add( "netgis-hide" );
+
+ this.parcelReset.classList.add( "netgis-hide" );
+
+ this.root.scrollTop = 0;
+};
+
+netgis.SearchParcel.prototype.onInputNameKey = function( e )
+{
+ switch ( e.keyCode )
+ {
+ // Enter
+ case 13:
+ {
+ this.selectFirstName();
+ break;
+ }
+
+ // Escape
+ case 27:
+ {
+ this.reset();
+ break;
+ }
+
+ default:
+ {
+ this.requestName( this.nameInput.value.trim() );
+ break;
+ }
+ }
+};
+
+netgis.SearchParcel.prototype.requestName = function( query )
+{
+ if ( this.nameDebounce ) window.clearTimeout( this.nameDebounce );
+
+ if ( query.length === 0 ) return;
+
+ /*var url = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_alkis/gem_search.php?placename={q}";
+ url = "/geoportal/client/proxy.php?" + url;*/
+ var url = this.client.config.searchParcel.nameURL;
+ url = netgis.util.replace( url, "{q}", window.encodeURIComponent( query ) );
+
+ this.nameDebounce = window.setTimeout( this.onInputNameDebounce.bind( this, url ), 200 );
+
+ this.nameLoader.classList.remove( "netgis-hide" );
+};
+
+netgis.SearchParcel.prototype.onInputNameDebounce = function( url )
+{
+ netgis.util.request( url, this.onInputNameResponse.bind( this ) );
+};
+
+netgis.SearchParcel.prototype.onInputNameResponse = function( data )
+{
+ this.nameLoader.classList.add( "netgis-hide" );
+
+ this.nameList.innerHTML = "";
+
+ if ( data.charAt( 0 ) !== '{' && data.charAt( 0 ) !== '[' ) return;
+
+ var json = JSON.parse( data );
+
+ for ( var i = 0; i < json.data.length; i++ )
+ {
+ var item = json.data[ i ];
+ var li = this.createNameItem( item[ "gmk_name" ] );
+ li.getElementsByTagName( "button" )[ 0 ].setAttribute( "data-id", item[ "gmk_gmn" ] );
+ this.nameList.appendChild( li );
+ }
+};
+
+netgis.SearchParcel.prototype.onNameItemClick = function( e )
+{
+ var button = e.target;
+ var id = button.getAttribute( "data-id" );
+
+ this.nameInput.value = button.innerHTML;
+ this.nameList.innerHTML = "";
+
+ this.districtInput.value = id;
+};
+
+netgis.SearchParcel.prototype.selectFirstName = function()
+{
+ var buttons = this.nameList.getElementsByTagName( "button" );
+
+ if ( buttons.length > 0 )
+ {
+ buttons[ 0 ].click();
+ }
+};
+
+netgis.SearchParcel.prototype.onParcelSearchClick = function( e )
+{
+ this.requestParcel
+ (
+ this.districtInput.value.trim(),
+ this.fieldInput.value.trim(),
+ this.parcelInputA.value.trim(),
+ this.parcelInputB.value.trim()
+ );
+};
+
+netgis.SearchParcel.prototype.requestParcel = function( district, field, parcelA, parcelB )
+{
+ //var url = "https://geodaten.naturschutz.rlp.de/kartendienste_naturschutz/mod_alkis/flur_search.php?gmk_gmn={district}&fln={field}&fsn_zae={parcelA}&fsn_nen={parcelB}&export=json";
+
+ var url = this.client.config.searchParcel.parcelURL;
+
+ url = netgis.util.replace( url, "{district}", district ? district : "" );
+ url = netgis.util.replace( url, "{field}", field ? field : "" );
+ url = netgis.util.replace( url, "{parcelA}", parcelA ? parcelA : "" );
+ url = netgis.util.replace( url, "{parcelB}", parcelB ? parcelB : "" );
+
+ //url = "/geoportal/client/proxy.php?" + url;
+
+ this.parcelTable.classList.add( "netgis-hide" );
+ this.parcelList.innerHTML = "";
+ this.parcelInfo.innerHTML = "Suche Flurstücke...";
+
+ netgis.util.request( url, this.onParcelResponse.bind( this ) );
+};
+
+netgis.SearchParcel.prototype.onParcelResponse = function( data )
+{
+ var json = JSON.parse( data );
+
+ if ( json.count === 0 )
+ {
+ //TODO: if ( json.count === 0 ) -> json.Info
+ this.parcelInfo.innerHTML = json[ "Info" ];
+ }
+ else
+ {
+ this.parcelInfo.innerHTML = "Flurstücke gefunden: " + json[ "count" ] + "";
+
+ for ( var i = 0; i < json.data.length; i++ )
+ {
+ var result = json.data[ i ];
+ var item = this.createParcelItem
+ (
+ result[ "fln" ],
+ result[ "fsn_zae" ],
+ result[ "fsn_nen" ],
+ result[ "fsk" ],
+ result[ "flaeche" ],
+ result[ "bbox" ],
+ result[ "geometry" ]
+ );
+ this.parcelList.appendChild( item );
+ }
+
+ this.parcelTable.classList.remove( "netgis-hide" );
+ }
+
+ this.parcelReset.classList.remove( "netgis-hide" );
+
+ if ( ! this.root.classList.contains( "netgis-hide" ) )
+ this.parcelTable.scrollIntoView();
+};
+
+netgis.SearchParcel.prototype.onParcelEnter = function( e )
+{
+ var tr = e.target;
+ var geom = tr.getAttribute( "data-geom" );
+
+ this.client.invoke( netgis.Events.PARCEL_SHOW_PREVIEW, { geom: geom } );
+};
+
+netgis.SearchParcel.prototype.onParcelLeave = function( e )
+{
+ this.client.invoke( netgis.Events.PARCEL_HIDE_PREVIEW, null );
+};
+
+netgis.SearchParcel.prototype.onParcelClick = function( e )
+{
+ var tr = e.currentTarget;
+ var bbox = tr.getAttribute( "data-bbox" );
+
+ this.client.invoke( netgis.Events.MAP_ZOOM_WKT, bbox );
+};
+
+netgis.SearchParcel.prototype.onParcelImportClick = function( e )
+{
+ var tr = e.currentTarget.parentElement.parentElement;
+ var geom = tr.getAttribute( "data-geom" );
+
+ this.client.invoke( netgis.Events.IMPORT_WKT, geom );
+};
+
+netgis.SearchParcel.prototype.onParcelResetClick = function( e )
+{
+ this.reset();
+};
+
+netgis.SearchParcel.prototype.onSetMode = function( e )
+{
+ if ( e === netgis.Modes.SEARCH_PARCEL && this.root.classList.contains( "netgis-hide" ) )
+ {
+ this.root.classList.remove( "netgis-hide" );
+ }
+ else
+ {
+ this.root.classList.add( "netgis-hide" );
+ }
+};
+
+netgis.SearchParcel.prototype.onLayerListToggle = function( e )
+{
+ this.root.classList.add( "netgis-hide" );
+};
\ No newline at end of file
diff --git a/src/netgis/Theme.css b/src/netgis/Theme.css
index bf97fd4..9bd2c47 100644
--- a/src/netgis/Theme.css
+++ b/src/netgis/Theme.css
@@ -14,6 +14,7 @@
.netgis-primary { background-color: #a7233f !important; color: white !important; }
.netgis-hover-primary:hover { background-color: #c82a4b !important; color: white !important; }
+.netgis-light { background-color: #f4f4f4 !important; }
.netgis-hover-light:hover { background-color: #f4f4f4 !important; }
.netgis-text-primary { color: #a7233f !important; }
.netgis-hover-text-primary:hover { color: #c82a4b !important; }