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; }