import DataUtils from '../utils/DataUtils';
import VerifyUtils from '../utils/VerifyUtils';
import GeogUtils from '../utils/GeogUtils';
import GMLProvider from '../geog/GMLProvider';
import LayerProvider from '../geog/LayerProvider';
import GeoJsonProvider from '../geog/GeoJsonProvider';
import SimpleMarkerLayer from '../common/SimpleMarkerLayer';
import LayerManagerProvider from './LayerManagerProvider';
import QueryResultsLayer from './QueryResultsLayer';

import { getApi } from '../../api/http';

/**
 * 地图查询提供类
 * @alias MapQueryProvider
 *
 * @summary <用于地图查询>
 */
export default class MapQueryProvider {
  /**
   * 构造函数
   * @param {Object} options -图层选项
   */
  constructor(options) {
    /**
     * 地图对象
     * @type {Object}
     */
    this._map = options.map;

    /**
     * 最小缩放层级
     * @type {Number}
     */
    this._minzoom = DataUtils.default(options.minzoom, 10);

    /**
     * 是否查询
     * @type {Boolean}
     */
    this._query = DataUtils.default(options.query, true);

    // 查询要素
    this._queryFeature = GeoJsonProvider.getEmptyFeature('Polygon');
    // 图层列表
    this._layers = [];

    this._closePointFC = GeoJsonProvider.getEmptyFC();

    /* 关闭点数据源ID */
    this._closeIcon = 'busi_close';

    this._closeMarkerSourceId = 'point_close_query';
    this._closeMarkerLayerId = this._closeMarkerSourceId;

    /* 图层对象 */
    this._queryLayer = null;   // 查询图层
    this._resultsLayer = null; // 结果图层

    // 关闭点图层
    this._closeMarkerLayer = new SimpleMarkerLayer({
      map: this._map,
      minzoom: this._minzoom,
      icon: this._closeIcon,
      offset: [-5, -5],
      sourceId: this._closeMarkerSourceId,
      layerId: this._closeMarkerLayerId
    });
  }

  /** 静态方法 **/
  /**
   * 查询服务
   * @param {Object} feature -查询要素
   * @param {Object} layer -图层对象
   * @param {Function} callback -回调函数
   */
  static async queryServer(layer, feature, callback) {
    const layerCode = layer.layerCode;
    const serverLayerName = layer.geolayerName; // 地图服务名
    if (!layer.wfsRootUrl ||
        !layerCode || !serverLayerName) {
      if (callback) {
        callback(null);
      }

      return;
    }

    const geomType = feature.geometry.type.toLowerCase();
    // 获取折线/多边形外环的坐标串
    const polyline = geomType === 'linestring'
                     ? feature.geometry.coordinates
                     : feature.geometry.coordinates[0];
    const coordsStr = GMLProvider.getLineStringString(polyline, false);
    // 获取折线/多边形外环筛选器
    const filter = geomType === 'linestring'
                   ? GMLProvider.getLineStringIntersectsFilter(coordsStr)
                   : GMLProvider.getPolygonOuterRingIntersectsFilter(coordsStr);

    let gmlFilter = filter;
    // 获取图层查询属性
    const property = LayerManagerProvider.getLayerQueryProperty(layerCode);
    if (property) {
      // 获取属性+折线/多边形外环相交查询筛选器
      gmlFilter = geomType === 'linestring'
                  ? GMLProvider.getPropertyEqualAndLineStringIntersectsFilter(property.name, property.value, coordsStr)
                  : GMLProvider.getPropertyEqualAndLinearRingIntersectsFilter(property.name, property.value, coordsStr);
    }

    // 拼接WFS查询URL
    let wfsQueryUrl = layer.wfsRootUrl + serverLayerName;
    wfsQueryUrl += '&filter=';
    wfsQueryUrl += gmlFilter;

    let resultFeatures = [];
    // 查询地图服务
    await getApi(wfsQueryUrl).then(res => {
      const fc = res.data;
      if (fc && fc.features && fc.features.length > 0) {
        // 获取要素主键字段名
        const fieldName = LayerManagerProvider.getLayerFieldName(layerCode);
        if (fieldName) {
          // 获取唯一字段值列表
          fc.features = GeoJsonProvider.getUniqueFeatures(fc.features, fieldName);
        }

        for (let j = 0; j < fc.features.length; j++) {
          const geometry = fc.features[j].geometry;
          const properties = fc.features[j].properties;

          properties.id = fieldName ? properties[fieldName] : undefined;
          properties.layercode = layerCode;
          properties.select = 0;

          // 添加要素
          resultFeatures.push({
            type: 'Feature',
            id: DataUtils.uuid(),
            geometry: geometry,
            properties: properties,
          });
        }
      }
    });

    if (callback) {
      callback(resultFeatures);
    }
  }

  /** 公有方法 **/
  /**
   * 设置查询图层对象列表
   * @param {Array} layers -图层对象列表
   */
  setLayers(layers) {
    this._layers = layers;
  }

  /**
   * 设置查询数据
   * @param {Object|Array} data -GeoJSON要素集/要素/数据
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  setQueryData(data, type) {
    if (!data) return;

    if (!this._queryLayer) {
      // 创建查询图层
      this._queryLayer = new QueryResultsLayer({
        map: this._map,
        minzoom: this._minzoom
      });
    }
    // 设置数据
    this._queryLayer.setData(data, type);
  }

  /**
   * 添加查询数据
   * @param {Object|Array} data -GeoJSON要素集/要素/数据
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  appendQueryData(data, type) {
    if (!data) return;

    if (!this._queryLayer) {
      // 创建查询图层
      this._queryLayer = new QueryResultsLayer({
        map: this._map,
        minzoom: this._minzoom
      });

      const fc = GeoJsonProvider.getEmptyFC();
      // 设置空数据
      this._queryLayer.setData(fc, type);
    }
    // 添加数据
    this._queryLayer.appendData(data, type);
  }

  /**
   * 绑定关闭点单击事件
   * @param {Function} event -事件函数
   */
  bindCloseMarkerClick(event) {
    this._closeMarkerLayer.onClick(event);
  }

  /**
   * 设置关闭点数据
   * @param {Object} data -GeoJSON要素集
   */
  setCloseMarkerData(data) {
    if (!VerifyUtils.isGeoJsonFC(data)) return;

    this._closePointFC.features = data.features;
    // 设置数据
    this._closeMarkerLayer.setData(this._closePointFC);
  }

  /**
   * 添加关闭点
   * @param {String} fid -要素ID
   * @param {Array} lonLat -坐标
   */
  appendCloseMarker(fid, lonLat) {
    if (!fid || !lonLat) return;

    let hasFeature = false;
    for (let i = 0; i < this._closePointFC.features.length; i++) {
      const feature = this._closePointFC.features[i];
      if (feature.properties.fid === fid) {
        feature.geometry.coordinates = lonLat;
        hasFeature = true;
      }
    }

    // 创建点要素
    if (!hasFeature) {
      const feature = GeoJsonProvider.getPointFeature(lonLat, {fid: fid});
      feature.id = fid;

      this._closePointFC.features.push(feature);
    }

    // 设置数据
    this._closeMarkerLayer.setData(this._closePointFC);
  }

  /**
   * 设置结果数据
   * @param {Object|Array} data -GeoJSON要素集/要素/数据
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  setResultsData(data, type) {
    if (!data) return;

    if (!this._resultsLayer) {
      // 创建结果图层
      this._resultsLayer = new QueryResultsLayer({
        map: this._map,
        minzoom: this._minzoom,
        isQuery: false
      });
    }
    // 设置数据
    this._resultsLayer.setData(data, type);
  }

  /**
   * 添加结果数据
   * @param {Object|Array} data -GeoJSON要素集/要素/数据
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  appendResultsData(data, type) {
    if (!data) return;

    if (!this._resultsLayer) {
      // 创建结果图层
      this._resultsLayer = new QueryResultsLayer({
        map: this._map,
        minzoom: this._minzoom,
        isQuery: false
      });

      const fc = GeoJsonProvider.getEmptyFC();
      // 设置空数据
      this._resultsLayer.setData(fc, type);
    }
    // 添加数据
    this._resultsLayer.appendData(data, type);
  }

  /**
   * 更新查询数据
   * @param {Object} data -GeoJSON要素集/要素
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  updateQueryData(data, type) {
    if (this._queryLayer) {
      this._queryLayer.updateData(data, type);
    }
  }

  /**
   * 是否存在查询数据
   * @param {Object} fid -要素ID
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  hasQueryData(fid, type) {
    if (this._queryLayer) {
      return this._queryLayer.hasData(fid, type);
    }

    return false;
  }

  /**
   * 是否存在结果数据
   * @param {String} rangeId -区域ID
   * @param {String} type -几何类型(取值：Point、LineString、Polygon; and Dot)
   */
  hasResultData(rangeId, type) {
    if (this._resultsLayer) {
      return this._resultsLayer.hasPropertyData('rangeId', rangeId, type);
    }

    return false;
  }

  /**
   * 获取查询数据
   * @param {Object} fid -要素ID
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, dot)
   */
  getQueryData(fid, type) {
    if (this._queryLayer) {
      return this._queryLayer.getData(fid, type);
    }

    return null;
  }

  /**
   * 删除查询数据
   * @param {Object} fid -要素ID
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, Dot)
   */
  deleteQueryData(fid) {
    if (this._queryLayer) {
      this._queryLayer.deleteData(fid, 'Polygon');
      this._queryLayer.deleteData(fid, 'LineString');
      this._queryLayer.deleteData(fid, 'Point');
    }

    if (this._closeMarkerLayer) {
      // 更新数据
      const features = this._closePointFC.features.filter(fea => {
        return fea.id != fid;
      });

      this._closePointFC.features = features;
      // 设置数据
      this._closeMarkerLayer.setData(this._closePointFC);
    }
  }

  /**
   * 删除结果数据
   * @param {Object} fid -要素ID
   */
  deleteResultsData(fid) {
    if (this._resultsLayer) {
      this._resultsLayer.deleteData(fid, 'Polygon');
      this._resultsLayer.deleteData(fid, 'LineString');
      this._resultsLayer.deleteData(fid, 'Point');
      this._resultsLayer.deleteData(fid, 'Dot');
    }
  }

  /**
   * 选择结果数据
   * @param {String} fid -要素ID
   * @param {String} type -几何类型(取值：Point、LineString、Polygon, Dot)
   */
  selectResultsData(fid, type) {
    if (this._resultsLayer) {
      this._resultsLayer.selectData(fid, type);
    }
  }

  /**
   * 清空结果数据
   */
  clearResultsData() {
    if (this._resultsLayer) {
      this._resultsLayer.clearData();
    }
  }

  /**
   * 清空查询数据
   */
  clearQueryData() {
    if (this._queryLayer) {
      this._queryLayer.clearData();
    }

    if (this._closeMarkerLayer) {
      this._closePointFC.features = [];
      this._closeMarkerLayer.setData(this._closePointFC);
    }
  }

  /**
   * 清空关闭点数据
   */
  clearCloseMarkerData() {
    if (this._closeMarkerLayer) {
      this._closePointFC.features = [];
      this._closeMarkerLayer.setData(this._closePointFC);
    }
  }

  /**
   * 获取查询要素
   *
   * @returns {Object}
   */
  getQueryFeature() {
    return this._queryFeature;
  }

  /**
   * 获取查询图层ID
   * @param {String} type -几何类型(取值：Point、LineString、Polygon)
   */
  getQueryLayerId(type) {
    if (!this._queryLayer) return null;

    return this._queryLayer.getLayerId(type);
  }

  /**
   * 缓冲区查询
   * @param {Object} data -GeoJSON要素/要素集
   * @param {Number} radius -半径 (单位：kilometers)
   *
   * @returns {Array} GeoJSON要素列表
   */
  bufferQuery(data, radius) {
    if (!this._layers || this._layers.length === 0) {
      return null;
    }

    // 清空查询数据
    this.clearQueryData();

    let feature;
    if (VerifyUtils.isGeoJsonFC(data)) {
      feature = data.features[0];
    } else if (VerifyUtils.isGeoJsonFeature(data)) {
      feature = data;
    }
    if (!feature) return null;

    let geomType = feature.geometry.type.toLowerCase();
    if (geomType === 'polygon') {
      return null;
    }

    // 生成缓冲区
    let bufferFea = GeogUtils.buffer(feature, radius);
    // 坐标保留小数点后6位
    const ring = bufferFea.geometry.coordinates[0];
    ring.forEach((cs) => {
      cs[0] = parseFloat(cs[0].toFixed(6));
      cs[1] = parseFloat(cs[1].toFixed(6));
    });

    // 设置要素ID、查询方式
    let fid = data.id ? data.id : DataUtils.uuid();
    feature.id = fid;
    feature.properties.fid = fid;
    feature.properties.method = 'buffer';

    bufferFea.id = fid;
    bufferFea.properties.fid = fid;
    bufferFea.properties.method = 'buffer';

    // 设置查询要素
    this.setQueryData(feature, feature.geometry.type);
    this.setQueryData(bufferFea, bufferFea.geometry.type);

    this._queryFeature = bufferFea;
    // 返回查询结果
    return this._queryMap(bufferFea);
  }

  /**
   * 空间查询
   * @param {Object} data -GeoJSON要素/要素集
   *
   * @returns {Array} GeoJSON要素列表
   */
  spatialQuery(data) {
    if (!this._layers || this._layers.length === 0) {
      return null;
    }

    // 清空查询数据
    this.clearQueryData();

    let feature;
    if (VerifyUtils.isGeoJsonFC(data)) {
      feature = data.features[0];
    } else if (VerifyUtils.isGeoJsonFeature(data)) {
      feature = data;
    }
    if (!feature) return null;

    let geomType = feature.geometry.type.toLowerCase();
    if (geomType !== 'point' && geomType !== 'polygon') {
      return null;
    }
    // 点要素转多边形要素
    if (geomType === 'point') {
      feature = GeoJsonProvider.point2PolygonFeature(feature);
    }

    // 设置要素ID、查询方式
    let fid = data.id ? data.id : DataUtils.uuid();
    feature.id = fid;
    feature.properties.fid = fid;
    feature.properties.method = feature.geometry.type;
    // 设置查询要素
    this.setQueryData(feature, feature.geometry.type);

    this._queryFeature = feature;
    // 返回查询结果
    return this._queryMap(feature);
  }

  /**
   * 缓冲区查询(地图服务)
   * @param {Object} data -GeoJSON要素/要素集
   * @param {Number} radius -半径 (单位：kilometers)
   * @param {Boolean} append -是否添加
   * @param {Function} callback -回调函数
   */
  bufferQueryServer(data, radius, append, callback) {
    if (!this._layers || this._layers.length === 0) {
      return;
    }

    let feature;
    if (VerifyUtils.isGeoJsonFC(data)) {
      feature = data.features[0];
    } else if (VerifyUtils.isGeoJsonFeature(data)) {
      feature = data;
    }
    if (!feature) return;

    let geomType = feature.geometry.type.toLowerCase();
    if (geomType === 'polygon') {
      return;
    }

    // 生成缓冲区
    let bufferFea = GeogUtils.buffer(feature, radius);
    // 坐标保留小数点后6位
    const ring = bufferFea.geometry.coordinates[0];
    ring.forEach((cs) => {
      cs[0] = parseFloat(cs[0].toFixed(6));
      cs[1] = parseFloat(cs[1].toFixed(6));
    });

    let fid = data.id ? data.id : DataUtils.uuid();
    // 设置要素ID、查询方式
    feature.id = fid;
    feature.properties.fid = fid;
    feature.properties.method = 'buffer';

    bufferFea.id = fid;
    bufferFea.properties.fid = fid;
    bufferFea.properties.method = 'buffer';

    if (append) {
      // 删除查询要素
      if (this.hasQueryData(fid, feature.geometry.type) ||
          this.hasQueryData(fid, bufferFea.geometry.type)) {
        this.deleteQueryData(fid);
      }
      // 添加查询要素
      this.appendQueryData(feature, feature.geometry.type);
      this.appendQueryData(bufferFea, bufferFea.geometry.type);
    } else {
      // 设置查询要素
      this.setQueryData(feature, feature.geometry.type);
      this.setQueryData(bufferFea, bufferFea.geometry.type);
    }

    this._queryFeature = bufferFea;
    // heyukun mod 2023-3-7
    /*
    // 查询地图服务
    this._queryServer(bufferFea, callback);
    */
    if (this._query) {
      // 查询地图服务
      this._queryServer(bufferFea, callback);
    } else {
      callback([]);
    }
  }

  /**
   * 空间查询
   * @param {Object} data -GeoJSON要素/要素集
   * @param {Boolean} append -是否添加
   * @param {Function} callback -回调函数
   */
  spatialQueryServer(data, append, callback) {
    if (!this._layers || this._layers.length === 0) {
      return null;
    }

    let feature;
    if (VerifyUtils.isGeoJsonFC(data)) {
      feature = data.features[0];
    } else if (VerifyUtils.isGeoJsonFeature(data)) {
      feature = data;
    }
    if (!feature) return null;

    let geomType = feature.geometry.type.toLowerCase();
    if (geomType !== 'linestring' &&
        geomType !== 'point' && geomType !== 'polygon') {
      return null;
    }
    // 点要素转多边形要素
    if (geomType === 'point') {
      feature = GeoJsonProvider.point2PolygonFeature(feature);
    }

    // 设置要素ID、查询方式
    let fid = data.id ? data.id : DataUtils.uuid();
    feature.id = feature.id || fid;
    feature.properties.fid = feature.properties.fid || fid;
    feature.properties.method = feature.geometry.type;
    if (append) {
      // 添加查询要素
      this.appendQueryData(feature, feature.geometry.type);
    } else {
      // 设置查询要素
      this.setQueryData(feature, feature.geometry.type);
    }

    this._queryFeature = feature;
    // heyukun mod 2023-3-7
    /*
    // 查询地图服务
    this._queryServer(feature, callback);
    */
    if (this._query) {
      // 查询地图服务
      this._queryServer(feature, callback);
    } else {
      callback([]);
    }
  }

  /**
   * 移除所有图层
   */
  removeAllLayers() {
    // 查询图层
    if (this._queryLayer) {
      this._queryLayer.removeLayer();
    }

    // 结果图层
    if (this._resultsLayer) {
      this._resultsLayer.removeLayer();
    }

    // 关闭点
    if (this._closeMarkerLayer) {
      this._closePointFC.features = [];
      this._closeMarkerLayer.removeLayer();
      this._closeMarkerLayer.removeSource();
    }
  }

  /** 私有方法 **/
  /**
   * 查询地图
   * @param {Object} data -GeoJSON面要素
   *
   * @return {Array} 查询结果GeoJSON要素列表
   */
  _queryMap(data) {
    if (!VerifyUtils.isGeoJsonFeature(data) ||
        data.geometry.type.toLowerCase() !== 'polygon') {
      return null;
    }

    const map = this._map;
    // 计算边界框
    const bbox = GeogUtils.calcBBox(data);
    if (!bbox) return null;

    // 经纬度转屏幕像素边界框
    let bottomLeftPnt = map.project(bbox[0]);
    let upperRightPnt = map.project(bbox[1]);
    let pixelBBox = [[bottomLeftPnt.x, upperRightPnt.y], [upperRightPnt.x, bottomLeftPnt.y]];

    const rangeId = data.id; // 查询要素ID

    let resultFeatures = [];
    for (let i = 0; i < this._layers.length; i++) {
      const layer = this._layers[i];

      const layerCode = layer.layercode; // 图层编码

      const layerIds = [];
      const layerId = layer.layerid;
      // 添加查询的图层
      if (LayerManagerProvider.isLayerShowLevel(layerCode)) {
        // 分级的图层(manhole、pipeline)
        for (let l = 1; l < 4; l++) {
          let id = layerId + '-lvl' + l.toString();
          if (LayerProvider.hasLayer(map, id)) {
            layerIds.push(id);
          }
        }
      } else if (layerCode === 'drainer' || layerCode === 'drainentity') {
        // 排水户
        layerIds.push(layerId + '-uncluster');
      } else if (LayerProvider.hasLayer(map, layerId)) {
        layerIds.push(layerId);
      }

      if (layerIds.length === 0) continue;

      // 查询地图要素列表
      let features = map.queryRenderedFeatures(pixelBBox, {
        layers: layerIds
      });
      if (!features || features.length === 0) continue;

      // 获取要素主键字段名
      const fieldName = LayerManagerProvider.getLayerFieldName(layerCode);
      // 空间查询
      if (layer.layertype === 'symbol') {
        // 点图层
        let tmpFC = GeoJsonProvider.getEmptyFC();
        tmpFC.features = features;
        if (fieldName) {
          tmpFC.features = GeoJsonProvider.getUniqueFeatures(tmpFC.features, fieldName);
        }

        // 查询在多边形内的点集
        let fc = GeogUtils.calcPointsWithinPolygon(tmpFC, data);
        features = fc.features;
      } else if (layer.layertype === 'line') {
        // 线图层
        if (fieldName) {
          features = GeoJsonProvider.getUniqueFeatures(features, fieldName);
        }
        for (let j = features.length - 1; j >= 0; j--) {
          // 判断要素是否与缓冲区相离
          if (GeogUtils.isDisjoint(data, features[j])) {
            features.splice(j, 1);
          }
        }
      }

      for (let k = 0; k < features.length; k++) {
        features[k].properties.id = fieldName ? features[k].properties[fieldName] : undefined;
        features[k].properties.rangeId = rangeId;
        features[k].properties.layercode = layerCode;
        features[k].properties.select = 0;

        // 添加要素
        resultFeatures.push({
          type: 'Feature',
          id: DataUtils.uuid(),
          properties: features[k].properties,
          geometry: features[k].geometry
        });
      }
    } // end of for (let i = 0; ...)

    return resultFeatures;
  }

  /**
   * 查询服务
   * @param {Object} data -GeoJSON面要素
   * @param {Function} callback -回调函数
   */
  async _queryServer(data, callback) {
    const map = this._map;
    const wfsRootUrl = map._wfsQueryUrl;

    const geomType = data.geometry.type.toLowerCase();
    // 获取折线/多边形外环的坐标串
    const polyline = geomType === 'linestring'
                     ? data.geometry.coordinates
                     : data.geometry.coordinates[0];
    const coordsStr = GMLProvider.getLineStringString(polyline, false);
    // 获取折线/多边形外环筛选器
    const filter = geomType === 'linestring'
                   ? GMLProvider.getLineStringIntersectsFilter(coordsStr)
                   : GMLProvider.getPolygonOuterRingIntersectsFilter(coordsStr);

    const rangeId = data.id; // 查询要素ID

    let resultFeatures = [];
    for (let i = 0; i < this._layers.length; i++) {
      let layer = this._layers[i];

      const layerCode = layer.layercode;
      const serverLayerName = layer.geolayerName; // 地图服务名
      if (!serverLayerName) continue;

      let gmlFilter = filter;
      // 获取图层查询属性
      const property = LayerManagerProvider.getLayerQueryProperty(layerCode);
      if (property) {
        // 获取属性+折线/多边形外环相交查询筛选器
        gmlFilter = geomType === 'linestring'
                    ? GMLProvider.getPropertyEqualAndLineStringIntersectsFilter(property.name, property.value, coordsStr)
                    : GMLProvider.getPropertyEqualAndLinearRingIntersectsFilter(property.name, property.value, coordsStr);
      }

      // 拼接WFS查询URL
      let wfsQueryUrl = wfsRootUrl + serverLayerName;
      wfsQueryUrl += '&filter=';
      wfsQueryUrl += gmlFilter;

      // 查询地图服务
      await getApi(wfsQueryUrl).then(res => {
        const fc = res.data;
        if (fc && fc.features && fc.features.length > 0) {
          // 获取要素主键字段名
          const fieldName = LayerManagerProvider.getLayerFieldName(layerCode);
          if (fieldName) {
            // 获取唯一字段值列表
            fc.features = GeoJsonProvider.getUniqueFeatures(fc.features, fieldName);
          }

          const features = fc.features;
          for (let k = 0; k < features.length; k++) {
            features[k].properties.id = fieldName ? features[k].properties[fieldName] : undefined;
            features[k].properties.rangeId = rangeId;
            features[k].properties.layercode = layerCode;
            features[k].properties.select = 0;

            // 添加要素
            resultFeatures.push({
              type: 'Feature',
              id: DataUtils.uuid(),
              properties: features[k].properties,
              geometry: features[k].geometry
            });
          }
        }
      });
    } // end of for (...)

    if (callback) {
      callback(resultFeatures);
    }
  }
}
