/**
* BeNomad BeMap JavaScript API - Geocoder
*/
/**
* @classdesc
* Base class for geocoder.
* @public
* @constructor
* @abstract
* @param {bemap.Context} context BeMap-JS-API Context. Mandatory.
* @param {object} options see below the available values.
* @param {String} options.language Define the language that will be used to perform the address lookup.
* @param {Int} options.maxResult The maximum number of items used to perform the research and returned items by the server. Default is 1.
* @param {bemap.BoundingBox} options.boundingBox the bounding box that will limit the research to a specific area.
*/
bemap.Geocoder = function(context, options) {
bemap.Service.call(this, context, options);
var opts = options || {};
/**
* @type {String}
* @protected
*/
this.language = opts.language ? opts.language : null;
/**
* @type {int}
* @protected
*/
this.maxResult = opts.maxResult ? opts.maxResult : 1;
/**
* @type {bemap.BoundingBox}
* @protected
*/
this.bbox = opts.boundingBox ? opts.boundingBox : null;
};
bemap.inherits(bemap.Geocoder, bemap.Service);
/**
* Get the language.
* @return {String} language
*/
bemap.Geocoder.prototype.getLanguage = function() {
return this.language;
};
/**
* Get the number of max results.
* @return {Int} maxResult
*/
bemap.Geocoder.prototype.getMaxResult = function() {
return this.maxResult;
};
/**
* Get the bounding box of the research.
* @return {bemap.BoundingBox} bbox
*/
bemap.Geocoder.prototype.getBoundingBox = function() {
return this.bbox;
};
/**
* Set the language of the research.
* @param {String} language the new language to set.
* @return {bemap.Geocoder} this.
*/
bemap.Geocoder.prototype.setLanguage = function(language) {
this.language = language;
return this;
};
/**
* Set the number of max results.
* @param {Int} maxResult the new number of max results.
* @return {bemap.Geocoder} this.
*/
bemap.Geocoder.prototype.setMaxResult = function(maxResult) {
this.maxResult = maxResult;
return this;
};
/**
* Set the bounding box of the research.
* @param {bemap.BoundingBox} bbox the new bounding box to set.
* @return {bemap.Geocoder} this.
*/
bemap.Geocoder.prototype.setBoundingBox = function(bbox) {
this.bbox = bbox;
return this;
};
/**
* Generate the BeMap request in URL encoded format.
* @private
* @param {object} options See below the available values.
* @param {object} options.geoserver Geoserver name will be used for this computation.
* @return {String} the request URL encoded.
*/
bemap.Geocoder.prototype.buildRequest = function(options) {
var opts = options || {};
var data = '&geoserver=' + (opts.geoserver ? opts.geoserver : this.ctx.getGeoserver());
var searchInfo = opts.searchInfo;
if (!searchInfo) {
return;
}
for (var prop in this) {
if (searchInfo.hasOwnProperty(prop) && searchInfo[prop]) {
if (prop === "bbox") {
data += "&" + prop + "=" + searchInfo[prop].minLon + "," + searchInfo[prop].minLat + "," + searchInfo[prop].maxLon + "," + searchInfo[prop].maxLat;
} else {
data += "&" + prop + "=" + encodeURI(searchInfo[prop]);
}
} else if (this[prop] && this.hasOwnProperty(prop) && prop !== 'ctx') {
if (prop === "bbox") {
data += "&" + prop + "=" + this[prop].minLon + "," + this[prop].minLat + "," + this[prop].maxLon + "," + this[prop].maxLat;
} else {
data += "&" + prop + "=" + encodeURI(this[prop]);
}
}
}
for (prop in searchInfo) {
if (searchInfo[prop] && searchInfo.hasOwnProperty(prop)) {
if (!this.hasOwnProperty(prop)) {
data += "&" + prop + "=" + encodeURI(searchInfo[prop]);
}
}
}
return data;
};
/**
* Execute the geocoding research.
* @public
* @param {object} options See below the available values.
* @param {bemap.RevGeoSearchInfo} options.searchInfo the information to to search.
* @param {object} options.geoserver Geoserver name will be used for this computation.
* @param {function} options.success the function to call in case of successed request.
* @param {function} options.failed the function to call in case of failed request.
* @return {bemap.Geocoder} this.
*/
bemap.Geocoder.prototype.revGeocode = function(options) {
//this.reset();
var opts = options || {};
if (!bemap.inheritsof(opts.searchInfo, bemap.RevGeoSearchInfo)) {
console.error("SearchInfo is required!");
}
var i = 0;
var first = true;
var url = this.ctx.getBaseUrl() + 'bnd';
var data = 'version=1.0.0&action=revgeocoding&format=json';
if (this.ctx.isAuthInPost()) {
data += '&' + this.ctx.getAuthUrlParams();
} else {
url += '?' + this.ctx.getAuthUrlParams();
}
data += this.buildRequest(opts);
return this.execute(url, data, opts);
};
/**
* Send a geocoding request to the bemap's server.
* @public
* @param {object} options See below the available values.
* @param {bemap.GeoSearchInfo} options.searchInfo the information to to search.
* @param {object} options.geoserver Geoserver name will be used for this computation.
* @param {function} options.success the function to call in case of successed request.
* @param {function} options.failed the function to call in case of failed request.
* @return {bemap.Geocoder} this.
*/
bemap.Geocoder.prototype.geocode = function(options) {
//this.reset();
var opts = options || {};
if (!bemap.inheritsof(opts.searchInfo, bemap.GeoSearchInfo)) {
console.error("SearchInfo is required!");
}
var i = 0;
var first = true;
var url = this.ctx.getBaseUrl() + 'bnd';
var data = 'version=1.0.0&action=geocoding&format=json';
if (this.ctx.isAuthInPost()) {
data += '&' + this.ctx.getAuthUrlParams();
} else {
url += '?' + this.ctx.getAuthUrlParams();
}
data += this.buildRequest(opts);
return this.execute(url, data, opts);
};
/**
* Excute the request by calling the BeMap server and wait the answer.
* @private
* @param {object} options Request options.
* @return {bemap.Routing} this
*/
bemap.Geocoder.prototype.execute = function(url, data, options) {
var opts = options || {};
var _this = this;
bemap.ajax(
'POST',
url,
data,
function(xhr, doc) {
_this.responseParser(xhr, doc, opts);
},
function(xhr, doc) {
_this.responseParser(xhr, doc, opts);
}, {
'requestFormat': 'urlencoded'
}
);
return this;
};
/**
* Convert the BeMap response to the BeMap JS API object.
* @private
**/
bemap.Geocoder.prototype.responseParser = function(xhr, doc, options) {
var opts = options || {};
doc = JSON.parse(doc);
if (this.checkErrorParser(xhr, doc, options)) {
return;
}
var bnd = doc.BND;
var elements = bnd.Elements.Element;
var response = new bemap.GeocodingResponse();
if (elements !== undefined) {
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var geocodingItem = new bemap.GeocodingItem();
for (var prop in element) {
var elementProp = element[prop];
if (!elementProp || !element.hasOwnProperty(prop)) {
continue;
}
//add id to easly get difference between elements
geocodingItem['index'] = i + 1;
if (prop === 'PostalAddress') {
var newPostalAddress = new bemap.PostalAddress({
countryCode: elementProp.CountryCode ? elementProp.CountryCode : '',
country: elementProp.Country ? elementProp.Country : '',
state: elementProp.State ? elementProp.State : '',
county: elementProp.County ? elementProp.County : '',
city: elementProp.City ? elementProp.City : '',
district: elementProp.District ? elementProp.District : '',
postalCode: elementProp.PostalCode ? elementProp.PostalCode : '',
street: elementProp.Street ? elementProp.Street : '',
streetNumber: elementProp.StreetNumber ? elementProp.StreetNumber : ''
});
geocodingItem[prop].push(newPostalAddress);
} else if (prop === 'Coordinate') {
var newCoordinate = new bemap.Coordinate(elementProp.x, elementProp.y);
geocodingItem[prop] = newCoordinate;
} else if (prop === 'RoadFeature') {
var newRoadFeature = new bemap.RoadFeature();
for (var inf in elementProp) {
newRoadFeature[inf] = elementProp[inf];
}
geocodingItem[prop].push(newRoadFeature);
} else {
geocodingItem[prop] = elementProp;
}
}
response.geocodingItems.push(geocodingItem);
}
} else {
console.error("The result of geocoding not found");
}
if (bnd.Extent) {
response.extent = new bemap.BoundingBox(bnd.Extent.minX, bnd.Extent.minY, bnd.Extent.maxX, bnd.Extent.maxY);
}
response.action = bnd.action;
if (opts.success) {
opts.success(response, doc, this, xhr);
}
};
/**
* Create table in selected div with data from parsing. Event click send by listener
* @public
* @param {Object} response data for create table.
* @param {object} options see below the available values.
* @param {DIV} container element to create table inside.
* @param {function} listener the function to call to get click data
*/
bemap.Geocoder.prototype.createTable = function(options, listener) {
//create list to store created markers
if (!bemap.markerMapObject) {
bemap.markerMapObject = [];
};
if (!options) {
console.error("Options required");
};
if (!options.container) {
console.error("Container required");
};
if (!options.response) {
console.error("Response required");
};
var container = options.container;
var doc = options.response;
container.empty();
var html = '<div><table class="table table-hover table-striped">';
html += '<thead><th>City</th><th>Country</th><th>PostalCode</th><th>Place</th></thead><tbody>';
for (var i = 0; i < doc.geocodingItems.length; i++) {
var e = doc.geocodingItems[i];
var p = e.PostalAddress[0];
html += '<tr class="cursorPointer" onclick="myFunction(this)" data="' + i + '">';
html += '<td>' + (p.city && p.city !== null ? p.city : '') + '</td>';
html += '<td>' + (p.country && p.country !== null ? p.country : '') + '</td><td>' + (p.postalCode && p.postalCode !== null ? p.postalCode : '') + '</td>';
html += '<td>' + (p.streetNumber && p.streetNumber !== null ? p.streetNumber : '') + ' ' + (p.street && p.street !== null ? p.street : '') + '</td></tr>';
}
html += '</tbody></table></div>';
var theDiv = document.getElementById(container[0].id);
theDiv.innerHTML += html;
//var theTr = document.querySelector('#' + container[0].id + ' tbody tr');
//used this solution because of deleting jquery
myFunction = function(elem) {
var index = elem.getAttribute('data');
var e = doc.geocodingItems[index];
if (listener) {
listener(e);
};
};
};
/**
* Parse data to create marker or markers on map
* @public
* @param {bemap.Map} map for creating new marker.
* @param {Object} response data to parse in.
* @param {object} options see below the available values.
* @param {function} listener the function to call to get click data
*/
bemap.Geocoder.prototype.showOnMap = function(options, listener) {
var map = options.map ? options.map : bemap.map;
var layer = options.layer ? options.layer : '';
if (!options) {
console.error("Options required");
}
if (!options.response) {
console.error("Response required");
}
var doc = options.response;
var icone = options.icone ? options.icone : {};
this.cleanMarkers();
//check if there is list of markers or only one marker
//if list make a for loop else send directly to create marker
if (doc.geocodingItems) {
for (var i = 0; i < doc.geocodingItems.length; i++) {
var e = doc.geocodingItems[i];
this.createMarker(map, e, icone, layer, function(data) {
if (listener) {
listener(data);
}
})
}
} else {
this.createMarker(map, doc, icone, layer, function(data) {
if (listener) {
listener(data);
};
});
};
};
/**
* Reset the geocoding marker object. Clear the previous result.
* @public
* @return {bemap.markerMapObject} this
*/
bemap.Geocoder.prototype.cleanMarkers = function() {
if (bemap.markerMapObject) {
for (i = 0; i < bemap.markerMapObject.length; i++) {
var marker = bemap.markerMapObject[i];
marker.remove();
marker = undefined;
};
bemap.markerMapObject = [];
} else {
bemap.markerMapObject = [];
};
};
/**
* Put markers on map
* @private
* @param {bemap.Map} map for creating new marker.
* @param {Object} data data to pcreate marker.
* @param {object} icone to personalize the marker.
* @param {function} listener the function to call to get click data
*/
bemap.Geocoder.prototype.createMarker = function(map, data, icone, layer, listener) {
map = map ? map : bemap.map;
icone = icone ? icone : {};
var posx = data.Coordinate.lon;
var posy = data.Coordinate.lat;
var c = new bemap.Coordinate(posx, posy);
var icon = new bemap.Icon({
src: icone.src ? icone.src : console.error("Check your icon adress REQUIRED ICON"),
anchorX: icone.anchorX ? icone.anchorX : 0.25,
anchorY: icone.anchorY ? icone.anchorY : 1,
height: icone.height ? icone.height : '',
width: icone.width ? icone.width : '',
anchorXUnits: icone.anchorXUnits ? icone.anchorXUnits : 'fraction',
anchorYUnits: icone.anchorYUnits ? icone.anchorYUnits : 'fraction',
scale: icone.scale ? icone.scale : 1
});
//before creating new markers check if there was already created
//this condition was made because of two posibilities of creation the markers
//first by list second one by one
if (bemap.markerMapObject) {
for (var i = 0; i < bemap.markerMapObject.length; i++) {
var mark = bemap.markerMapObject[i];
if (mark.id == data.index) {
map.move(posx, posy, 16);
return
};
};
};
var marker = new bemap.Marker(
c, {
properties: data,
icon: icon,
id: data.index
}
);
if (layer) {
map.addMarker(marker, {
layer: layer
});
} else {
map.addMarker(marker);
};
//save list of markers to clean it later or to prevent dubbles
bemap.markerMapObject.push(marker);
marker.on(bemap.Map.EventType.CLICK, function(mapEvent) {
if (listener) {
listener(mapEvent);
};
});
};