Skip to content

Commit

Permalink
Introduces WfsSearch
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiVolland committed Apr 9, 2018
1 parent dd602b0 commit 0fa5b04
Show file tree
Hide file tree
Showing 4 changed files with 535 additions and 0 deletions.
64 changes: 64 additions & 0 deletions src/Field/WfsSearch/WfsSearch.example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
This demonstrates the usage of the WfsSearch.

```jsx
const React = require('react');
const OlMap = require('ol/map').default;
const OlView = require('ol/view').default;
const OlLayerTile = require('ol/layer/tile').default;
const OlSourceOsm = require('ol/source/osm').default;
const OlProj = require('ol/proj').default;

class WfsSearchExample extends React.Component {

constructor(props) {

super(props);

this.mapDivId = `map-${Math.random()}`;

this.map = new OlMap({
layers: [
new OlLayerTile({
name: 'OSM',
source: new OlSourceOsm()
})
],
view: new OlView({
center: OlProj.fromLonLat([37.40570, 8.81566]),
zoom: 4
})
});
}

componentDidMount() {
this.map.setTarget(this.mapDivId);
}

render() {
return(
<div>
<div className="example-block">
<label>The WfsSerach<br />
<WfsSearch
baseUrl='https://ahocevar.com/geoserver/wfs'
searchAttributes={['natural', 'name']}
map={this.map}
style={{
width: '80%'
}}
/>
</label>
</div>
<div
id={this.mapDivId}
style={{
height: '400px'
}}
/>
</div>
)
}
}

<WfsSearchExample />
```
290 changes: 290 additions & 0 deletions src/Field/WfsSearch/WfsSearch.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
AutoComplete,
} from 'antd';
const Option = AutoComplete.Option;

import Logger from '../../Util/Logger';
import UrlUtil from '../../Util/UrlUtil/UrlUtil';
import olProj from 'ol/proj';
import OlMap from 'ol/map';

/**
* The WfsSearch.
*
* @class WfsSearch
* @extends React.Component
*/
export class WfsSearch extends React.Component {

/**
* The className added to this component.
* @type {String}
* @private
*/
className = 'react-geo-wfssearch'

static propTypes = {
className: PropTypes.string,
/**
* The base URL.
* @type {String}
*/
baseUrl: PropTypes.string.isRequired,
/**
* The list of attributes that should be searched through.
*/
searchAttributes: PropTypes.arrayOf(PropTypes.string).isRequired,
/**
* The namespace URI used for features. Required.
*/
featureNS: PropTypes.string.isRequired,
/**
* The prefix for the feature namespace. Required.
*/
featurePrefix: PropTypes.string.isRequired,
/**
* The feature type names. Required.
*/
featureTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
/**
* SRS name. No srsName attribute will be set on geometries when this is not
* provided.
*/
srsName: PropTypes.string,
/**
* Ther output format of the response.
*/
outputFormat: PropTypes.string,
/**
* Maximum number of features to fetch.
*/
maxFeatures: PropTypes.number,
/**
* Geometry name to use in a BBOX filter.
*/
geometryName: PropTypes.string,
/**
* Optional list of property names to serialize.
*/
propertyNames: PropTypes.arrayOf(PropTypes.string),
/**
* Wheather to use paging or not.
*/
paging: PropTypes.bool,
/**
* Start index to use for WFS paging. This is a WFS 2.0 feature backported
* to WFS 1.1.0 by some Web Feature Services.
*/
pagingStartIndex: PropTypes.number,
/**
* Number of features to retrieve when paging. This is a WFS 2.0 feature
* backported to WFS 1.1.0 by some Web Feature Services. Please note that
* some Web Feature Services have repurposed maxfeatures instead.
*/
pagingCount: PropTypes.number,
/**
* Filter condition. See http://openlayers.org/en/latest/apidoc/ol.format.filter.html
* for more information.
*/
filter: PropTypes.object,
/**
* The ol.map where the map will zoom to.
*
* @type {Object}
*/
map: PropTypes.instanceOf(OlMap).isRequired,
/**
* The minimal amount of characters entered in the input to start a search.
* @type {Number}
*/
minChars: PropTypes.number,
/**
* A render function which gets called with the selected item as it is
* returned by the server. It must return an `AutoComplete.Option`.
*
* @type {function}
*/
renderOption: PropTypes.func,
/**
* An onSelect function which gets called with the selected item as it is
* returned by nominatim.
* @type {function}
*/
onSelect: PropTypes.func
}

static defaultProps = {
paging: true,
pagingStartIndex: 0,
pagingCount: 20,
srsName: 'EPSG:3857',
outputFormat: 'application/json',
minChars: 3,
/**
* Create an AutoComplete.Option from the given data.
*
* @param {Object} item The tuple as an object.
* @return {AutoComplete.Option} The returned option
*/
renderOption: (item) => {
return (
<Option key={item.place_id}>
{item.display_name}
</Option>
);
},
/**
* The default onSelect method if no onSelect prop is given. It zooms to the
* selected item.
*
* @param {object} selected The selected item as it is returned by nominatim.
*/
onSelect: (selected, olMap) => {
if (selected && selected.boundingbox) {
const olView = olMap.getView();
debugger

extent = extent.map(function(coord) {
return parseFloat(coord);
});

extent = olProj.transformExtent(extent, 'EPSG:4326',
olView.getProjection().getCode());

olView.fit(extent, {
duration: 500
});
}
},
style: {
width: 200
}
}

/**
* Create the WfsSearch.
*
* @param {Object} props The initial props.
* @constructs WfsSearch
*/
constructor(props) {
super(props);
this.state = {
searchTerm: '',
dataSource: []
};
this.onUpdateInput = this.onUpdateInput.bind(this);
this.onMenuItemSelected = this.onMenuItemSelected.bind(this);
}

/**
* Called if the input of the AutoComplete is being updated. It sets the
* current inputValue as searchTerm and starts a search if the inputValue has
* a length of at least `this.props.minChars` (default 3).
*
* @param {String|undefined} inputValue The inputValue. Undefined if clear btn
* is pressed.
*/
onUpdateInput(inputValue) {
this.setState({
dataSource: []
});

this.setState({
searchTerm: inputValue || ''
}, () => {
if (this.state.searchTerm.length >= this.props.minChars) {
this.doSearch();
}
});
}

/**
* Perform the search.
*/
doSearch() {
this.state.searchTerm;

const featureRequest = new ol.format.WFS().writeGetFeature(this.props.options);
const getRequestParams = UrlUtil.objectToRequestString(baseParams);

fetch(`${this.props.baseUrl}${getRequestParams}`)
.then(response => response.json())
.then(this.onFetchSuccess.bind(this))
.catch(this.onFetchError.bind(this));
}

/**
* This function gets called on success of the nominatim fetch.
* It sets the response as dataSource.
*
* @param {Array<object>} response The found features.
*/
onFetchSuccess(response) {
this.setState({
dataSource: response
});
}

/**
* This function gets called when the nomintim fetch returns an error.
* It logs the error to the console.
*
* @param {String} error The errorstring.
*/
onFetchError(error) {
Logger.error(`Error while requesting Nominatim: ${error}`);
}

/**
* The function describes what to do when an item is selected.
*
* @param {value} key The key of the selected option.
*/
onMenuItemSelected(key) {
const selected = this.state.dataSource.filter(i => i.place_id === key)[0];
this.props.onSelect(selected, this.props.map);
}

/**
* The render function.
*/
render() {
const {
className,
nominatimBaseUrl,
format,
viewbox,
bounded,
polygon_geojson,
addressdetails,
limit,
countrycodes,
map,
onSelect,
renderOption,
...passThroughProps
} = this.props;

const finalClassName = className
? `${className} ${this.className}`
: this.className;

return (
<AutoComplete
className={finalClassName}
allowClear={true}
placeholder="Ortsname, Straßenname, Stadtteilname, POI usw."
dataSource={this.state.dataSource.map(renderOption.bind(this))}
optionLabelProp="display_name"
onChange={this.onUpdateInput}
onSelect={this.onMenuItemSelected}
{...passThroughProps}
/>
);
}
}

export default WfsSearch;
Loading

0 comments on commit 0fa5b04

Please sign in to comment.