Source: temp/jsdocinputdirs/cercalia.feature.js

/**
 * @constructor
 * @param {cercaliax.FeatureOptions} options Feature options
 */
cercalia.Feature = function(options) {

  /**
   * @private
   * @type {string}
   */
  this.id_ = options.id ? options.id : 'CercaliaFeature_id_' + Date.now() + (Math.random() * 0xffffffff | 0);

  /**
   * @private
   * @type {cercalia.Map}
   */
  this.map_ = null;

  /**
   * @private
   * @type {ol.Feature}
   */
  this.feature_ = null;

  /**
   * @private
   * @type {Array.<ol.Feature>}
   */
  this.arrowsFeatures_ = null;

  /**
   * @private
   * @type {string}
   */
  this.srs_ = options.srs ? options.srs : 'EPSG:4326';

  /**
   * @private
   * @type {ol.geom.Geometry}
   */
  this.geometry_ = options.geometry ? options.geometry : null;

  /**
   * @private
   * @type {number}
   */
  this.zIndex_ = options.zIndex ? options.zIndex : 100;

  /**
   * @private
   * @type {string}
   */
  this.strokeColor_ = options.strokeColor ? options.strokeColor : '#ffffff';

  /**
   * @private
   * @type {number}
   */
  this.strokeOpacity_ = options.strokeOpacity !== undefined ? options.strokeOpacity : 0.7;

  /**
   * @private
   * @type {number}
   */
  this.strokeWidth_ = options.strokeWidth !== undefined ? options.strokeWidth : 5;

  /**
   * @private
   * @type {boolean}
   */
  this.outline_ = options.outline ? options.outlineColor : 1;

  /**
   * @private
   * @type {string}
   */
  this.outlineColor_ = options.outlineColor ? options.outlineColor : '#009BFF';

  /**
   * @private
   * @type {number}
   */
  this.outlineOpacity_ = options.outlineOpacity !== undefined ? options.outlineOpacity : 0.9;

  /**
   * @private
   * @type {number}
   */
  this.outlineWidth_ = options.outlineWidth !== undefined ? options.outlineWidth : 7;

  /**
   * @private
   * @type {string}
   */
  this.fillColor_ = options.fillColor ? options.fillColor : '#B5E2FF';

  /**
   * @private
   * @type {number}
   */
  this.fillOpacity_ = options.fillOpacity !== undefined ? options.fillOpacity : 0.75;

  /**
   * @private
   * @type {string}
   */
  this.pointEditableColor_ = options.pointEditableColor ? options.pointEditableColor : '#009BFF';

  /**
   * @private
   * @type {number}
   */
  this.radius_ = options.radius ? options.radius : 5;

  /**
   * @type {Function}
   */
  this.onClick = options.onClick ? options.onClick : null;

  /**
   * @private
   * @type {Array.<number>}
   */
  this.lineDash_ = options.lineDash ? options.lineDash : null;

  /**
   * @type {Function}
   */
  this.onDoubleClick = options.onDoubleClick ? options.onDoubleClick : null;

  /**
   * @type {Function}
   */
  this.onRightClick = options.onRightClick ? options.onRightClick : null;

  /**
   * @type {Function}
   */
  this.onMouseOver = options.onMouseOver ? options.onMouseOver : null;

  /**
   * onMouseOver Function, if specified
   * @type {Function}
   */
  this.onMouseOut = options.onMouseOut ? options.onMouseOut : null;

  /**
   * @type {Function}
   * @private
   */
  this.onDragStartCallBack_ = options.onDragStart ? options.onDragStart : null;

  /**
   * @type {Function}
   * @private
   */
  this.onDragMoveCallBack_ = options.onDragMove ? options.onDragMove : null;

  /**
   * @type {Function}
   * @private
   */
  this.onDragEndCallBack_ = options.onDragEnd ? options.onDragEnd : null;

  /**
   * @type {Function}
   */
  this.onDragStart = function(marker, lonlat, evt) {
    if (this.onDragStartCallBack_) this.onDragStartCallBack_(marker, lonlat, evt);
  };

  /**
   * @type {Function}
   */
  this.onDragMove = function(marker, lonlat, evt) {
    this.isDragging_ = true;
    if (this.onDragMoveCallBack_) this.onDragMoveCallBack_(marker, lonlat, evt);
  };

  /**
   * @type {Function}
   */
  this.onDragEnd = function(marker, lonlat, evt) {
    this.isDragging_ = false;
    if (this.onDragEndCallBack_) this.onDragEndCallBack_(marker, lonlat, evt);
  };

  /**
   * @private
   * @type {boolean}
   */
  this.isDragging_ = false;

  /**
   * @type {funtion}
   */
  this.onDrop = options.onDrop ? options.onDrop : null;

  /**
   * @private
   * @type {cercalia.SimpleLabel}
   */
  this.simpleLabel_ = options.simpleLabel ? options.simpleLabel : null;

  if (this.simpleLabel_) {
    this.simpleLabel_.setFeature(this);
  }

  /**
   * @private
   * @type {boolean}
   */
  this.draggable_ = undefined;

  /**
   * @private
   * @type {boolean}
   */
  this.editable_ = options.editable ? options.editable : false;

  /**
   * @private
   * @type {string}
   */
  this.wkt_ = options.wkt ? options.wkt : null;

  /**
   * @private
   * @type {boolean}
   */
  this.showDirection_ = options.showDirection ? options.showDirection : false;

  /**
   * @private
   * @type {String}
   */
  this.iconArrow_ = options.iconArrow ? options.iconArrow : cercaliaGlobals.img + '/cercalia-arrow.png';

  /**
   * @private
   * @type {ol.style.Style}
   */
  this.style_ = null;

  /**
   * @private
   * @type {ol.style.Style}
   */
  this.styleEditable_ = null;

  /**
   * @private
   * @type {string}
   */
  this.prefixLayernameArrow_ = "LayerFeatureArrows";

  if (this.wkt_) {
    var wktFormat = new ol.format.WKT();
    this.feature_ = wktFormat.readFeature(options.wkt);
    //this.feature_ = this.features_[0];

    this.geometry_ = this.feature_.getGeometry();
    if (this.srs_ != 'EPSG:3857') {
      this.geometry_.transform(this.srs_, 'EPSG:3857'); //Per defecte EPSG:3857
    }
  } else if (this.geometry_) {
    this.feature_ = new ol.Feature({
      geometry: this.geometry_
    });
  }

  // Atencio! Afegim Id apart perque a vegades no es guarda
  this.feature_.setId(this.id_ + "_ol_feature");

  //Con esta funcion inicializamos el atribut 'this.style_'
  this.setStyle_();

  //Si se desa pintar las flechas sobre la linea
  if (this.showDirection_) {
    this.createArrows_();
  }

  this.feature_.setStyle(this.style_);

  /**
   * @private
   * @type {cercalia.Feature}
   */
  this.feature_.feature_ = this;

  /**
   * Class name
   * @private
   * @type {string}
   */
  this.CLASS_NAME_ = "cercalia.Feature";

  /**
   * @private
   * @type {string}
   */
  this.feature_.featureType_ = this.CLASS_NAME_;

  /**
   * @private
   * @type {boolean}
   */
  this.editab

  // Inicialitzem estats de drag i edit
  this.setDraggable(options.draggable ? options.draggable : false);
};


/**
 * @return {string} Internal feature ID
 */
cercalia.Feature.prototype.getId = function() {
  return this.id_;
};

/**
 * @return {cercalia.Map} Map object {@link cercalia.Map}
 */
cercalia.Feature.prototype.getMap = function() {
  return this.map_;
};

/**
 * Assign an ID to the feature.
 * @param {string} id
 */
cercalia.Feature.prototype.setId = function(id) {
  this.id_ = id;
};

/**
 * @return {string} Feature WKT (Well-known-text). WKT has not Circle type format.
 */
cercalia.Feature.prototype.getWKT = function() {
  //return this.wkt_;
  if (this.feature_) {
    return this.feature_.getWKT();
  } else {
    return this.wkt_;
  }
};


/**
 * Assigns the value to the style object based on geometry
 * @private
 * @param {string} styleModified
 */
cercalia.Feature.prototype.setStyle_ = function(styleModified) {

  switch (this.feature_.getGeometry().getType()) {
    case 'Point':
    case 'MultiPoint':
      this.optionsStyle_ = {
        text: this.simpleLabel_ ? this.simpleLabel_.getTextStyle() : null,
        image: new ol.style.Circle({
          radius: this.radius_,
          fill: new ol.style.Fill({
            color: cercalia.Util.transformColor2rgba(this.fillColor_, this.fillOpacity_)
          }),
          stroke: new ol.style.Stroke({
            color: cercalia.Util.transformColor2rgba(this.strokeColor_, this.strokeOpacity_),
            lineDash: this.lineDash_,
            width: this.strokeWidth_
          })
        })
      };
      break;
    case 'LineString':
    case 'MultiLineString':
    case 'Polygon':
    case 'MultiPolygon':
    case 'GeometryCollection':
    case 'Circle':
      this.optionsStyle_ = {
        text: this.simpleLabel_ ? this.simpleLabel_.getTextStyle() : null,
        stroke: new ol.style.Stroke({
          color: cercalia.Util.transformColor2rgba(this.strokeColor_, this.strokeOpacity_),
          lineDash: this.lineDash_,
          width: this.strokeWidth_
        }),
        fill: new ol.style.Fill({
          color: cercalia.Util.transformColor2rgba(this.fillColor_, this.fillOpacity_)
        }),
        zIndex: this.zIndex_
      };
      break;
    default:
      alert('Unknown geometry: ' + this.feature_.getGeometry().getType());
      break;
  }

  this.style_ = [new ol.style.Style(this.optionsStyle_)];

  if (this.outline_ != null) {
    this.style_.push(new ol.style.Style({
      stroke: new ol.style.Stroke({
        color: cercalia.Util.transformColor2rgba(this.outlineColor_, this.outlineOpacity_),
        width: this.outlineWidth_
      })
    }));
  }

  if (this.feature_) {
    this.feature_.setStyle(this.style_);
  }
};



/**
 * Converts the feature object to `draggable`. You can drag the feature.
 * @param {boolean} draggable
 */
cercalia.Feature.prototype.setDraggable = function(draggable) {
  var map = this.map_;
  if (this.map_) {
    this.map_.removeFeatures(this);
  }
  this.draggable_ = draggable;
  if (map) {
    map.addFeature(this);
  }
};

/**
 * @return {boolean} Returns the drag status for feature. Values: `true` or `false`.
 */
cercalia.Feature.prototype.isDraggable = function() {
  return this.draggable_;
};

/**
 * @return {boolean} Returns the edit status for feature. Values: `true` or `false`.
 */
cercalia.Feature.prototype.isEditable = function() {
  return this.editable_;
};

/**
 *  Modify the edit status for feature. Values: `true` or `false`.
 */
cercalia.Feature.prototype.setEditable = function(edit) {
  this.editable_ = edit;
  if (this.map_) {
    this.map_.setFeatureEditable(this, edit);
  }

  //If style editable not initialized..
  if (!this.styleEditable_) {
    this.styleEditable_ = this.style_.slice(0); //clone object
    var geom = this.feature_.getGeometry();
    if (geom instanceof ol.geom.Polygon || geom instanceof ol.geom.LineString) {
      this.styleEditable_.push(
        new ol.style.Style({
          image: new ol.style.Circle({
            radius: 5,
            fill: new ol.style.Fill({
              color: cercalia.Util.transformColor2rgba(this.pointEditableColor_, this.fillOpacity_)
            })
          }),
          geometry: function(feature) {
            // return the coordinates of the first ring of the polygon
            var coordinates;
            var geometry = feature.getGeometry();
            if (geometry instanceof ol.geom.Polygon) {
              coordinates = geometry.getCoordinates()[0];
            } else {
              coordinates = geometry.getCoordinates();
            }
            return new ol.geom.MultiPoint(coordinates);
          },
          zIndex: Infinity
        })
      );
    }
  }

  if (edit) {
    this.feature_.setStyle(this.styleEditable_);
  } else {
    this.feature_.setStyle(this.style_);
  }

};


/**
 * @return {number} zIndex Feature object
 */
cercalia.Feature.prototype.getZIndex = function() {
  return this.zIndex_;
};

/**
 * Assigns a zIndex to Feature object
 * @param {number} zIndex zIndex CSS.
 */
cercalia.Feature.prototype.setZIndex = function(zIndex) {
  this.zIndex_ = zIndex;
  this.setStyle_();
};

/**
 * @return {number} circle Radius (in pixels).
 */
cercalia.Feature.prototype.getRadius = function() {
  return this.radius_;
};

/**
 * Assigns the feature object outline opacity.
 * @param {number} radius
 */
cercalia.Feature.prototype.setRadius = function(radius) {
  this.radius_ = radius;
  this.setStyle_();
};

/**
 * @return {number} Feature opacity. Values: `[0..1]`.
 */
cercalia.Feature.prototype.getStrokeOpacity = function() {
  return this.strokeOpacity_;
};

/**
 * Assigns the feature object outline opacity. Feature.
 * @param {number} strokeOpacity Opacity, values between 0 y 1. ->  `[0..1]`
 */
cercalia.Feature.prototype.setStrokeOpacity = function(strokeOpacity) {
  this.strokeOpacity_ = strokeOpacity;
  this.setStyle_();
};

/**
 * @return {string} Outline color.
 */
cercalia.Feature.prototype.getStrokeColor = function() {
  return this.strokeColor_;
};

/**
 * Assign the object outiline color.
 * @param {string} strokeColor Object Color
 */
cercalia.Feature.prototype.setStrokeColor = function(strokeColor) {
  this.strokeColor_ = strokeColor;
  this.setStyle_();
};

/**
 * @return {number} Outline width
 */
cercalia.Feature.prototype.getStrokeWidth = function() {
  return this.strokeWidth_;
};

/**
 * @param {number} strokeWidth Outline width
 */
cercalia.Feature.prototype.setStrokeWidth = function(strokeWidth) {
  this.strokeWidth_ = strokeWidth;
  this.setStyle_();
};

/**
 * @return {number}  Feature object outline opacity.
 */
cercalia.Feature.prototype.getOutlineOpacity = function() {
  return this.outlineOpacity_;
};

/**
 * Assigns the object outline opacity. Feature.
 * @param {number} outlineOpacity feature outline opacity.
 */
cercalia.Feature.prototype.setOutlineOpacity = function(outlineOpacity) {
  this.outlineOpacity_ = outlineOpacity;
  this.setStyle_();
};

/**
 * @return {string} Outline color.
 */
cercalia.Feature.prototype.getOutlineColor = function() {
  return this.outlineColor_;
};

/**
 * Asign the outline color.
 * @param {string} outlineColor object color
 */
cercalia.Feature.prototype.setOutlineColor = function(outlineColor) {
  this.outlineColor_ = outlineColor;
  this.setStyle_();
};

/**
 * @return {number} Outline width
 */
cercalia.Feature.prototype.getOutlineWidth = function() {
  return this.outlineWidth_;
};

/**
 * @param {number} outlineWidth outiline width
 */
cercalia.Feature.prototype.setOutlineWidth = function(outlineWidth) {
  this.outlineWidth_ = outlineWidth;
  this.setStyle_();
};

/**
 * @return {string} Fill color
 */
cercalia.Feature.prototype.getFillColor = function() {
  return this.fillColor_;
};

/**
 * Assign the fillcolor.
 * @param {string} Fill color Hexadecimal color. Ex: '#ff0000'
 */
cercalia.Feature.prototype.setFillColor = function(fillColor) {
  this.fillColor_ = fillColor;
  this.setStyle_();
};

/**
 * @return {number} Fill opacity. Values between 0 and 1.
 */
cercalia.Feature.prototype.getFillOpacity = function() {
  return this.fillOpacity_;
};

/**
 * Assign Fill opacity. Values between 0 and 1.
 * @param {number} fillOpacity Fill opacity. Values between 0 and 1.
 */
cercalia.Feature.prototype.setFillOpacity = function(fillOpacity) {
  this.fillOpacity_ = fillOpacity;
  this.setStyle_();
};

/**
 * Assign Line dash.
 * @param {Array.<number>} lineDash Line dash.
 */
cercalia.Feature.prototype.setLineDash = function(lineDash) {
  this.lineDash_ = lineDash;
  this.setStyle_();
};

/**
 * @return {Array.<number>} Line dash.
 */
cercalia.Feature.prototype.getLineDash = function() {
  return this.lineDash_;
};



/**
 * True if feature is dragging, otherwise false.
 * @return {boolean}
 */
cercalia.Feature.prototype.isDragging = function() {
  return this.isDragging_;
}


/**
 * @return {ol.Feature} Object <a href="http://openlayers.org/en/v3.0.0/apidoc/ol.Feature.html">`ol.Feature`</a> Openlayers3
 */
cercalia.Feature.prototype.getFeature = function() {
  return this.feature_;
};

/**
 * SimpleLabel to add/modify related to the marker
 * @param {cercalia.SimpleLabel} simpleLabel
 */
cercalia.Feature.prototype.setSimpleLabel = function(simpleLabel) {
  this.simpleLabel_ = simpleLabel;
  this.simpleLabel_.setFeature(this);
  this.optionsStyle_.text = simpleLabel.getTextStyle();
  this.setStyle_();
};

/**
 * @return {cercalia.SimpleLabel} Returns {@link cercalia.SimpleLabel} if it's created. If not, returns `null`.
 */
cercalia.Feature.prototype.getSimpleLabel = function() {
  return this.simpleLabel_;
};

/**
 * Add or remove map reference. For add a feature, use the class {@link cercalia.Map}
 * function `addFeature` or `addFeatures`
 * @param {cercalia.Map} map Cercalia Map.
 */
cercalia.Feature.prototype.setMap = function(map) {
  if (map) {
    if (this.map_ == null) {
      this.map_ = map;

      var projectionCode = map.getProjectionCode();

      //Si té una altra projecció que no és la original
      if (projectionCode != 'EPSG:3857') {
        this.getFeature().getGeometry().transform('EPSG:3857', projectionCode);
      }

      //Arrows si en té..
      if (this.arrowsFeatures_) {
        this.addArrowsToMap_();
      }

      this.setEditable(this.editable_);

    } else {
      alert('cercalia.feature: Ya existía el Feature');
    }
  } else {
    this.removeArrowsFromMap_();
    this.map_ = null;
  }

};

/**
 * @private
 */
cercalia.Feature.prototype.removeArrowsFromMap_ = function() {
  if (this.map_ && this.clusterArrowLayer_) {
    this.map_.removeLayer(this.clusterArrowLayer_);
    this.clusterArrowLayer_ = null;
    this.arrowsFeatures_ = null;
  }
};

/**
 * @private
 */
cercalia.Feature.prototype.addArrowsToMap_ = function() {

  var sourceFeatures = new ol.source.Vector({});
  this.clusterArrowLayer_ = new ol.layer.Vector({
    name: this.prefixLayernameArrow_ + this.id_,
    source: new ol.source.Cluster({
      distance: 40,
      source: new ol.source.Vector({
        features: this.arrowsFeatures_
      })
    })
  });

  // Afegim el layer sota el layer de markers per que aquests apareguin per sobre les fletxes
  var indexToPut = this.map_.getLayerIndexByName(cercalia.LayerEnumeration.MarkerLayer);
  this.map_.addLayer(this.clusterArrowLayer_, indexToPut);
  var self = this;

  sourceFeatures.addFeatures(this.arrowsFeatures_);

  var onchangeSourceFunction = function() {
    var clusterFeatures = self.clusterArrowLayer_.getSource().getFeatures();
    if (clusterFeatures.length > 0) {
      for (var i = 0; i < clusterFeatures.length; i++) {
        //Por cada Cluster (feature) buscamos el elemento de los de dentro más cercano
        var clusterInnerFeatures = clusterFeatures[i].getProperties().features;
        if (clusterInnerFeatures.length == 1) {
          //Solo tiene una feature. Usaremos esta como flecha
          clusterFeatures[i].setGeometry(clusterInnerFeatures[0].getGeometry());
          clusterFeatures[i].setStyle(clusterInnerFeatures[0].getStyle());
        } else {
          //Buscamos la feature mas cercana al cluster para usar su estilo y su posicion
          var coordReferencia = clusterFeatures[i].getGeometry().getCoordinates();
          var minDist = 9999999;
          var featureNearest = null;
          for (var k = 0; k < clusterInnerFeatures.length; k++) {
            var coordAct = clusterInnerFeatures[k].getGeometry().getCoordinates();
            var dist = Math.sqrt(Math.pow((coordReferencia[0] - coordAct[0]), 2) + Math.pow((coordReferencia[1] - coordAct[1]), 2));
            if (dist < minDist) {
              minDist = dist;
              featureNearest = clusterInnerFeatures[k];
            }
          }
          //Feature más cercana
          clusterFeatures[i].setGeometry(featureNearest.getGeometry());
          clusterFeatures[i].setStyle(featureNearest.getStyle());
        }
      }
    }
    self.clusterArrowLayer_.getSource().once('change', onchangeSourceFunction);
  };

  this.clusterArrowLayer_.getSource().once('change', onchangeSourceFunction);


};


/**
 *  Destroy object and related atributes to free up memory
 */
cercalia.Feature.prototype.destroy = function() {
  if (this.map_ != null) {
    this.removeArrowsFromMap_();
    this.map_.removeFeatures(this);
    for (var key in this) {
      delete this[key];
    }
  }
};

/**
 * @return {cercalia.Bounds} Returns the feature limits
 */
cercalia.Feature.prototype.getBounds = function() {
  var extentOL = this.feature_.getGeometry().getExtent();
  var projectionCode = this.map_ ? this.map_.getProjectionCode() : 'EPSG:3857';
  var extent = ol.proj.transformExtent(extentOL, projectionCode, 'EPSG:4326');
  return new cercalia.Bounds(extent);
};

/**
 * Returns the object type.
 * @return {string}
 */
cercalia.Feature.prototype.getClass = function() {
  return this.CLASS_NAME_;
};

/**
 * @private
 */
cercalia.Feature.prototype.createArrows_ = function() {
  var geometry = this.feature_.getGeometry();
  var position = 'middle';
  var forEachSegment = true;

  if (geometry instanceof ol.geom.LineString) {
    this.arrowsFeatures_ = this.createLineStringDirection_(geometry, position, forEachSegment);
  } else if (geometry instanceof ol.geom.MultiLineString) {

    //Convertimos multilinestring a linestring
    var linestrings = geometry.getLineStrings();
    var linestringGeometry = linestrings[0];
    for (var i = 1; i < linestrings.length; i++) {
      linestrings[i].getCoordinates().forEach(function(coord, index) {
        linestringGeometry.appendCoordinate(coord);
      });
    }
    this.arrowsFeatures_ = this.createLineStringDirection_(linestringGeometry, position, forEachSegment);
  }
};

/**
 * @private
 * @param {ol.geom.LineString} line
 * @param {String} position
 * @param {boolean} forEachSegment
 * @return {Array.<ol.Feature>}
 */
cercalia.Feature.prototype.createLineStringDirection_ = function(line, position, forEachSegment) {

  if (position == undefined) {
    position = "end";
  }
  if (forEachSegment == undefined) {
    forEachSegment = false;
  }
  var points = [];
  var allSegs = this.getSegments_(line);
  var segs = [];

  if (forEachSegment) {
    segs = allSegs;
  } else {
    if (position == "start") {
      segs.push(allSegs[0]);
    } else if (position == "end") {
      segs.push(allSegs[allSegs.length - 1]);
    } else if (position == "middle") {
      return [this.getPointOnLine_(line, .5)];
    } else {
      return [];
    }
  }

  for (var i = 0; i < segs.length; i++) {
    points = points.concat(this.createSegDirection_(segs[i], position));
  }
  return points;
};

/**
 * @private
 * @param {ol.geom.LineString} line
 * @return {Array.<Object>}
 */
cercalia.Feature.prototype.getSegments_ = function(line) {
  var numSeg = line.getCoordinates().length - 1;
  var components = line.getCoordinates();
  var segments = new Array(numSeg),
    point1, point2;
  for (var i = 0; i < numSeg; ++i) {
    point1 = components[i];
    point2 = components[i + 1];
    segments[i] = {
      x1: point1[0],
      y1: point1[1],
      x2: point2[0],
      y2: point2[1]
    };
  }
  return segments;
};

/**
 * @private
 * @param {ol.Coord} seg
 * @param {String} position
 * @return {Array.<ol.Feature>}
 */
cercalia.Feature.prototype.createSegDirection_ = function(seg, position) {

  var segBearing = this.bearing_(seg);
  var positions = [];
  var points = [];

  if (position == "start") {
    positions.push([seg.x1, seg.y1]);
  } else if (position == "end") {
    positions.push([seg.x2, seg.y2]);
  } else if (position == "middle") {
    positions.push([(seg.x1 + seg.x2) / 2, (seg.y1 + seg.y2) / 2]);
  } else {
    return null;
  }

  for (var i = 0; i < positions.length; i++) {

    var idFeature = this.id_ + "_arrow_" + new Date().getTime() + "" + Math.random() * 10000;
    var pt = new ol.geom.Point([positions[i][0], positions[i][1]]);
    var ptFeature = new ol.Feature({
      geometry: pt
    });
    ptFeature.setId(idFeature);

    ptFeature.setStyle(new ol.style.Style({
      image: new ol.style.Icon({
        src: this.iconArrow_,
        rotation: (Math.PI * segBearing / 180),
        rotateWithView: true
      }),
      zIndex: this.zIndex_ + 1
    }));

    points.push(ptFeature);

  }

  return points;
};

/**
 * @private
 * @param {ol.Coord} seg
 * @return {number}
 */
cercalia.Feature.prototype.bearing_ = function(seg) {
  var b_x = 0;
  var b_y = 1;
  var a_x = seg.x2 - seg.x1;
  var a_y = seg.y2 - seg.y1;
  var angle_rad = Math.acos((a_x * b_x + a_y * b_y) / Math.sqrt(a_x * a_x + a_y * a_y));
  var angle = 360 / (2 * Math.PI) * angle_rad;
  if (a_x < 0) {
    return 360 - angle;
  } else {
    return angle;
  }
};

/**
 * @private
 * @param {ol.geom} line
 * @param {number} measure
 * @return {ol.Feature|boolean}
 */
cercalia.Feature.prototype.getPointOnLine_ = function(line, measure) {

  var segs = this.getSegments_(line);
  var lineLength = line.getLength();
  var measureLength = lineLength * measure;
  var length = 0;
  var partLength = 0;
  for (var i = 0; i < segs.length; i++) {
    var segLength = this.getSegmentLength_(segs[i]);

    if (measureLength < length + segLength) {

      partLength = measureLength - length;

      var x = segs[i].x1 + (segs[i].x2 - segs[i].x1) * partLength / segLength;
      var y = segs[i].y1 + (segs[i].y2 - segs[i].y1) * partLength / segLength;
      var segBearing = bearing(segs[i]);
      var pt = new ol.geom.Point(x, y);

      var ptFeature = new ol.Feature({
        geometry: pt
      });
      ptFeature.setId(this.id_ + "_ol_feature" + "_p_" + i);
      ptFeature.setStyle(new ol.style.Style({
        image: new ol.style.Icon({
          src: this.iconArrow_,
          rotation: segBearing
        })
      }));

      return ptFeature;
    }

    length = length + segLength;

  }

  return false;
};


/**
 * Returns true if the feature has directions created and visibles.
 */
cercalia.Feature.prototype.hasDirections = function() {
  return this.arrowsFeatures_ != null;
};

/**
 * Show arrow directions of feature
 */
cercalia.Feature.prototype.showDirections = function() {
  if (!this.showDirection_) {
    this.showDirection_ = true;
    this.createArrows_();
    if (this.arrowsFeatures_) {
      this.addArrowsToMap_();
    }
  }
};

/**
 *  Hide arrow directions of feature.
 */
cercalia.Feature.prototype.hideDirections = function() {
  if (this.showDirection_) {
    this.showDirection_ = false;
    this.removeArrowsFromMap_();
  }
};

/**
 * @private
 * @param {Object} seg
 * @return {number}
 */
cercalia.Feature.prototype.getSegmentLength_ = function(seg) {
  return Math.sqrt(Math.pow((seg.x2 - seg.x1), 2) + Math.pow((seg.y2 - seg.y1), 2));
};

/**
 * @public
 * @param onDragStart {function|undefined}
 */
cercalia.Feature.prototype.addOnDragStart = function(onDragStart) {
  this.onDragStartCallBack_ = onDragStart ? onDragStart : null;
};

/**
 * @public
 * @param onDragMove {function|undefined}
 */
cercalia.Feature.prototype.addOnDragMove = function(onDragMove) {
  this.onDragMoveCallBack_ = onDragMove ? onDragMove : null;
};


/**
 * @public
 * @param onDragEnd {function|undefined}
 */
cercalia.Feature.prototype.addOnDragEnd = function(onDragEnd) {
  this.onDragEndCallBack_ = onDragEnd ? onDragEnd : null;
};

/**
 * @public
 * @param onClick {function|undefined}
 */
cercalia.Feature.prototype.addOnClick = function(onClick) {
  this.onClick = onClick ? onClick : null;
};


/**
 * @return {string} Change vertices color when is in editing mode.
 */
cercalia.Feature.prototype.setEditablePointColor = function(color) {
  this.pointEditableColor_ = color;
};

/**
 * @param {cercalia.LonLat} position Position
 * @return {cercalia.LonLat} Closest position of feature respect parameter position.
 */
cercalia.Feature.prototype.getClosestCoordinate = function(position) {
  var projectionCode = this.map_ ? this.map_.getProjectionCode() : 'EPSG:3857';
  var olCoordinate = position.transform(projectionCode);
  var closestCoordinate = this.geometry_.getClosestPoint(olCoordinate);
  return new cercalia.LonLat(closestCoordinate[0], closestCoordinate[1], projectionCode);
};