Commit 25816f69 authored by Noel Alonso's avatar Noel Alonso
Browse files

Cambia el planteamiento para obtener windrose

Con el planteamiento anterior, obtener datos con un rango de fechas
amplio podía dar problemas al superar los límites establecidos por
elasticsearch.
parent 1aa5baa7
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
package es.redmic.timeseriesview.converter;

import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import es.redmic.elasticsearchlib.common.utils.ElasticSearchUtils;
import es.redmic.exception.common.NoContentException;
import es.redmic.models.es.geojson.common.model.Aggregations;
import es.redmic.timeseriesview.dto.windrose.LimitsDTO;
import es.redmic.timeseriesview.dto.windrose.WindRoseDataDTO;
import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.metadata.Type;

@Component
public class WindRoseDataConverter extends CustomConverter<Aggregations, WindRoseDataDTO> {

	private Integer numSectors;

	private Integer partitionNumber;

	protected final static Logger LOGGER = LoggerFactory.getLogger(WindRoseDataConverter.class);

	@SuppressWarnings("unchecked")
	@Override
	public WindRoseDataDTO convert(Aggregations source, Type<? extends WindRoseDataDTO> destinationType,
			MappingContext mappingContext) {

		// @formatter:off
		
		numSectors = (Integer) mappingContext.getProperty("numSectors");
		
		partitionNumber = (Integer) mappingContext.getProperty("partitionNumber");

		Map<String, Object> stats = ElasticSearchUtils.getMapValue(
				ElasticSearchUtils.getMapValue(source.getAttributes(), "filter#dataDefinitionFilter"),
					"stats#speed_stats");
		
		List<Map<String, Object>> values = (List<Map<String, Object>>) 
				(ElasticSearchUtils.getMapValue(source.getAttributes(), "date_histogram#avg_values_by_interval"))
					.get("buckets");
		
		if (values == null || values.size() == 0) {
			LOGGER.error("No es posible realizar los cálculos. No se ha obtenido resultados");
			throw new NoContentException();
		}
		
		Double sectorLength = 360.0 / numSectors;
		
		Double rotationOffset = sectorLength / 2;
		
		// @formatter:on

		Double max = (Double) stats.get("max");

		// Eliminar max
		WindRoseDataDTO windRoseDataDTO = new WindRoseDataDTO(values.size(), max, partitionNumber, numSectors);

		List<LimitsDTO> limits = windRoseDataDTO.getLimits();

		for (int i = 0; i < values.size(); i++) {

			// @formatter:off
			
			Map<String, Object> directionValue = ElasticSearchUtils.getMapValue(
				ElasticSearchUtils.getMapValue(values.get(i), "filter#directionDataDefinitionFilter"),
					"avg#avg_direction"),
				speedData = ElasticSearchUtils.getMapValue(values.get(i), "filter#speedDataDefinitionFilter");
			
			Double direction = (Double) directionValue.get("value"),
					speed = (Double) ElasticSearchUtils.getMapValue(speedData, "avg#avg_speed").get("value");
			
			if (direction != null && speed != null) {
			
				Integer sectorIndex = getSectorIndex(direction, sectorLength, rotationOffset),
					splitIndex = getSplitIndex(speed, limits);
				
				windRoseDataDTO.getData().get(sectorIndex).get(splitIndex).addCount();
			}
			else {
				LOGGER.info("Dirección o velocidad con valores nulos en la agregación", values.get(i).toString());
			}
		}

		// @formatter:on

		// Se calculan los porcentajes con respecto al total de todos los splits de cada
		// uno de los sectores
		windRoseDataDTO.calculate();

		return windRoseDataDTO;
	}

	/**
	 * Devuelve el índice dentro del array de sectores que le corresponde al dato
	 */
	private Integer getSectorIndex(Double value, Double sectorLength, Double rotationOffset) {

		if (((value >= (360 - rotationOffset)) && (value <= 360))
				|| ((value >= 0) && (value < (sectorLength - rotationOffset)))) {
			return 0;
		}

		double limit = sectorLength;
		for (int i = 1; i < numSectors; i++) {

			if ((value >= (limit - rotationOffset)) && (value < (limit + rotationOffset))) {
				return i;
			}
			limit += sectorLength;
		}

		LOGGER.warn("No se ecuentra un sector donde clasificar el siguiente valor de dirección: ", value);
		return null;
	}

	/**
	 * Devuelve el índice dentro del array de divisiones que le corresponde al dato
	 */
	private Integer getSplitIndex(Double value, List<LimitsDTO> limits) {

		for (int i = 0; i < limits.size(); i++) {
			if ((value >= limits.get(i).getMin()) && ((value < limits.get(i).getMax()) || (i == (limits.size() - 1)))) {
				return i;
			}
		}
		LOGGER.warn("No se ecuentra un split donde clasificar el siguiente valor de velocidad: ", value);
		return null;
	}
}
+69 −0
Original line number Diff line number Diff line
package es.redmic.timeseriesview.dto.windrose;

import java.util.ArrayList;
import java.util.List;

public class WindRoseDataDTO extends RangesOfSplitsDTO {

	private List<WindRoseSectorDTO> data = new ArrayList<WindRoseSectorDTO>();

	private Integer numSectors;

	private Integer partitionNumber;

	public WindRoseDataDTO() {
		super();
	}

	public WindRoseDataDTO(Integer total, Double max, Integer partitionNumber, Integer numSectors) {
		super();
		this.numSectors = numSectors;
		this.partitionNumber = partitionNumber;
		initialize(total);
		setLimits(max);
	}

	private void initialize(Integer total) {

		for (int i = 0; i < numSectors; i++) {
			data.add(new WindRoseSectorDTO(total, partitionNumber));
		}
	}

	public List<WindRoseSectorDTO> getData() {
		return data;
	}

	public void setData(List<WindRoseSectorDTO> data) {
		this.data = data;
	}

	public void addSectorData(WindRoseSectorDTO sectorData) {

		if (data == null)
			data = new ArrayList<WindRoseSectorDTO>();
		data.add(sectorData);
	}

	private void setLimits(Double max) {

		List<LimitsDTO> limits = new ArrayList<LimitsDTO>();

		double partitionLength = (Math.ceil(max) - 0) / partitionNumber;

		double limit = 0;
		for (int i = 0; i < partitionNumber; i++) {
			limits.add(new LimitsDTO(limit, limit + partitionLength));
			limit += partitionLength;
		}

		this.setLimits(limits);
	}

	public void calculate() {

		for (int i = 0; i < numSectors; i++) {
			data.get(i).calculate();
		}
	}
}
+38 −219
Original line number Diff line number Diff line
package es.redmic.timeseriesview.repository;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BaseAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator.KeyedFilter;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import es.redmic.elasticsearchlib.timeseries.repository.RTimeSeriesESRepository;
import es.redmic.exception.common.ExceptionType;
import es.redmic.exception.common.InternalException;
import es.redmic.exception.common.NoContentException;
import es.redmic.models.es.common.query.dto.AggsPropertiesDTO;
import es.redmic.models.es.common.query.dto.DataQueryDTO;
import es.redmic.models.es.data.common.model.DataSearchWrapper;
import es.redmic.timeseriesview.common.query.SeriesQueryUtils;
import es.redmic.timeseriesview.dto.windrose.DatesByDirectionListDTO;
import es.redmic.timeseriesview.dto.windrose.LimitsDTO;
import es.redmic.timeseriesview.dto.windrose.WindroseDataDTO;
import es.redmic.timeseriesview.dto.windrose.WindroseSectorDTO;
import es.redmic.timeseriesview.dto.windrose.WindRoseDataDTO;
import es.redmic.timeseriesview.model.timeseries.TimeSeries;
import es.redmic.viewlib.config.MapperScanBeanItfc;
import ma.glasnost.orika.MappingContext;

@Repository
public class WindRoseESRepository extends RTimeSeriesESRepository<TimeSeries, DataQueryDTO> {
@@ -42,170 +37,20 @@ public class WindRoseESRepository extends RTimeSeriesESRepository<TimeSeries, Da
	@Autowired
	protected MapperScanBeanItfc mapper;

	@SuppressWarnings({ "serial", "unchecked" })
	public Map<String, Object> getStatAggs(DataQueryDTO query, Integer speedDataDefinition) {

		// Crea query para obtener max, min y count de velocidades
		query.setSize(0);
		query.getTerms().put("dataDefinition", new ArrayList<Integer>() {
			{
				add(speedDataDefinition);
			}
		});
		// añade identificador para que se cree la agregación correspondiente
		query.addAgg(new AggsPropertiesDTO("stats", "speed"));

		DataSearchWrapper<TimeSeries> responseStats = (DataSearchWrapper<TimeSeries>) find(query);

		if (noContent(responseStats)) {
			LOGGER.error("No es posible realizar los cálculos. No se ha obtenido resultados");
			throw new NoContentException();
		}

		return getAttributeFromAggregations(responseStats, "stats#value");
	}

	private boolean noContent(DataSearchWrapper<TimeSeries> response) {

		boolean nullResult = response.getAggregations() == null || response.getAggregations().getAttributes() == null;
		return (nullResult || getAttributeFromAggregations(response, "stats#value").get("count").equals(0));
	}

	@SuppressWarnings({ "unchecked" })
	private Map<String, Object> getAttributeFromAggregations(DataSearchWrapper<TimeSeries> response, String attribute) {
		return (Map<String, Object>) response.getAggregations().getAttributes().get(attribute);
	}

	@SuppressWarnings({ "serial", "unchecked" })
	public DatesByDirectionListDTO getDatesByDirectionAggs(DataQueryDTO query, Integer numSectors,
			Integer directionDataDefinition) {

		// añade identificador para que se cree la agregación de los rangos de
		// los sectores (Los sectores se calculan en grados a partir del número de
		// sectores enviado por el cliente)
		query.getAggs().clear();
		query.addAgg(new AggsPropertiesDTO("sectors", "sectors"));
		query.getTerms().put("numSectors", numSectors);

		query.getTerms().put("dataDefinition", new ArrayList<Integer>() {
			{
				add(directionDataDefinition);
			}
		});

		DataSearchWrapper<TimeSeries> responseSectors = (DataSearchWrapper<TimeSeries>) find(query);

		if (responseSectors == null || responseSectors.getAggregations() == null) {
			LOGGER.debug("No es posible realizar los cálculos");
			throw new InternalException(ExceptionType.INTERNAL_EXCEPTION);
		}

		return mapper.getMapperFacade().convert(responseSectors.getAggregations(), DatesByDirectionListDTO.class, null,
				null);
	}

	@SuppressWarnings({ "serial" })
	public WindroseDataDTO getWindroseData(DataQueryDTO query, DatesByDirectionListDTO datesByDirectionListDTO,
			Integer speedDataDefinition, Map<String, Object> stats, Integer partitionNumber) {

		// @formatter:off
		
		Double min = (Double) stats.get("min"),
				max = (Double) stats.get("max");
		
		// @formatter:on

		Integer count = (Integer) stats.get("count");

		WindroseDataDTO windroseDataDTO = new WindroseDataDTO(min, max, partitionNumber);

		query.getAggs().clear();
		query.addAgg(new AggsPropertiesDTO("windrose"));

		query.getTerms().put("limits", windroseDataDTO.getLimits());

		query.getTerms().put("dataDefinition", new ArrayList<Integer>() {
			{
				add(speedDataDefinition);
			}
		});

		List<SearchSourceBuilder> searchs = new ArrayList<SearchSourceBuilder>();

		// para cada uno de los sectores obtener queryBuilder enviando en terms
		// las fechas.
		for (int i = 0; i < datesByDirectionListDTO.size(); i++) {
			// TODO: optimizar evitando hacer query cuando
			// datesByDirectionListDTO.get(i).getDates() == 0
			query.getTerms().put("dates", datesByDirectionListDTO.get(i).getDates());
			searchs.add(searchRequestBuilder(query));
		}
		List<DataSearchWrapper<?>> results = multiFind(searchs);

		// para cada respuesta obtenemos los resultados de la agregación
		for (int i = 0; i < results.size(); i++) {
			WindroseSectorDTO dataSector = mapper.getMapperFacade().convert(results.get(i).getAggregations(),
					WindroseSectorDTO.class, null, null);
			dataSector.calculateValue(count);
			windroseDataDTO.addSectorData(dataSector);
		}
		return windroseDataDTO;
	}

	/**
	 * Sobrescribe el método original para añadir las agregaciones específicas para
	 * windrose
	 */
	@Override
	protected SearchSourceBuilder searchRequestBuilder(DataQueryDTO queryDTO, QueryBuilder serviceQuery) {
		SearchSourceBuilder searchSourceBuilder = super.searchRequestBuilder(queryDTO, serviceQuery);

		List<BaseAggregationBuilder> aggs = getAggs(queryDTO);

		if (aggs != null) {
			for (BaseAggregationBuilder term : aggs) {
				searchSourceBuilder.aggregation((AggregationBuilder) term);
			}
		}

		return searchSourceBuilder;
	}

	/**
	 * Método que sustituye a getAggs original ya que necesita el objeto query
	 * completo para fabricar las agregaciones
	 * 
	 * Método que realiza la consulta y manda a convertir el resultado al dto
	 * esperado
	 */
	protected List<BaseAggregationBuilder> getAggs(DataQueryDTO queryDTO) {

		List<AggsPropertiesDTO> aggs = queryDTO.getAggs();

		if (queryDTO.getInterval() == null && (aggs == null || aggs.size() == 0))
			return null;

		List<BaseAggregationBuilder> aggsBuilder = new ArrayList<BaseAggregationBuilder>();
	@SuppressWarnings("unchecked")
	public WindRoseDataDTO getWindRoseData(DataQueryDTO query, Integer numSectors, Integer partitionNumber) {

		switch (aggs.get(0).getField()) {
		case "stats":
			// Primera agregación: estadísticas para obtener el min, max y count de la
			// velocidad
			aggsBuilder.add(AggregationBuilders.stats(defaultField).field(defaultField));
			break;
		case "sectors":
			// Segunda agregación: Fechas agregadas por sectores que corresponden con la
			// dirección.
			aggsBuilder.add(getSectorAggregationBuilder(queryDTO));
			break;
		case "windrose":
			// Tercera agregación: Velocidad agregada por las fechas que corresponden con
			// cada sector
			aggsBuilder.add(getWindroseAggregationBuilder(queryDTO));
			break;
		default:
			break;
		}
		Map<Object, Object> globalProperties = new HashMap<Object, Object>();
		globalProperties.put("numSectors", numSectors);
		globalProperties.put("partitionNumber", partitionNumber);

		return aggsBuilder;
		return mapper.getMapperFacade().convert(((DataSearchWrapper<TimeSeries>) find(query)).getAggregations(),
				WindRoseDataDTO.class, null, new MappingContext(globalProperties));
	}

	/**
@@ -214,64 +59,38 @@ public class WindRoseESRepository extends RTimeSeriesESRepository<TimeSeries, Da
	 */
	@Override
	protected List<BaseAggregationBuilder> getAggs(List<AggsPropertiesDTO> aggs) {
		return null;
	}

	/**
	 * Obtener fechas de datos agrupadas por sectores (Query 2 de windrose)
	 */
	private FiltersAggregationBuilder getSectorAggregationBuilder(DataQueryDTO elasticQueryDTO) {

		Integer numSectors = (Integer) elasticQueryDTO.getTerms().get("numSectors");

		// @formatter:off
		String listSplitRegex = "\\s*,\\s*";

		Double sectorLength = 360.0 / numSectors,
				rotationOffset = sectorLength / 2;
		List<BaseAggregationBuilder> aggBuilders = new ArrayList<BaseAggregationBuilder>();

		// @formatter:on
		List<String> speedDataDefinitions = Arrays.asList(aggs.get(0).getTerm().split(listSplitRegex)),
				directionDataDefinitions = Arrays.asList(aggs.get(1).getTerm().split(listSplitRegex));

		KeyedFilter[] filters = new KeyedFilter[numSectors];
		TermsQueryBuilder speedTermQuery = QueryBuilders.termsQuery("dataDefinition", speedDataDefinitions);

		double limit = 0;
		for (int i = 0; i < numSectors; i++) {
		// Se consultan las estadísticas para crear los límites
		aggBuilders.add(AggregationBuilders.filter("dataDefinitionFilter", speedTermQuery)
				.subAggregation(AggregationBuilders.stats("speed_stats").field(defaultField)));

			if (limit == 0) {
				BoolQueryBuilder filter = QueryBuilders.boolQuery();
				filter.should().add(0, QueryBuilders.rangeQuery(defaultField).gte(360 - rotationOffset).lte(360));
				filter.should().add(1, QueryBuilders.rangeQuery(defaultField).gte(0).lt(sectorLength - rotationOffset));
				filters[i] = new KeyedFilter(i + "", filter); // se guarda como índice el orden en la representación
			} else {
				filters[i] = new KeyedFilter(i + "",
						QueryBuilders.rangeQuery(defaultField).gte(limit - rotationOffset).lt(limit + rotationOffset));
			}
			limit += sectorLength;
		}

		return AggregationBuilders.filters("direction_ranges", filters)
				.subAggregation(AggregationBuilders.terms("dates").field(dateTimeField).size(MAX_SIZE));
	}
		// Desde el cliente se envía en ms y aquí se pasa a seg
		int timeInterval = (int) (Long.parseLong(aggs.get(2).getTerm()) / 1000);

	/**
	 * Obtener count de velocidad agrupadas por rangos de precisión (Query final por
	 * cada sector)
	 * 
	 */
	@SuppressWarnings("unchecked")
	private RangeAggregationBuilder getWindroseAggregationBuilder(DataQueryDTO elasticQueryDTO) {
		// Se consultan direcciones y velocidades agregadas por el timeInterval definido

		List<LimitsDTO> limits = (List<LimitsDTO>) elasticQueryDTO.getTerms().get("limits");
		AggregationBuilder directionValues = AggregationBuilders
				.filter("directionDataDefinitionFilter",
						QueryBuilders.termsQuery("dataDefinition", directionDataDefinitions))
				.subAggregation(AggregationBuilders.avg("avg_direction").field(defaultField));

		RangeAggregationBuilder range = AggregationBuilders.range("value_ranges").field(defaultField);
		AggregationBuilder speedValues = AggregationBuilders.filter("speedDataDefinitionFilter", speedTermQuery)
				.subAggregation(AggregationBuilders.avg("avg_speed").field(defaultField));

		for (int i = 0; i < limits.size(); i++) {
			if (i == (limits.size() - 1)) // Rango abierto para el último
				range.addUnboundedFrom(limits.get(i).getMin());
			else
				range.addRange(limits.get(i).getMin(), limits.get(i).getMax());
		}
		aggBuilders.add(AggregationBuilders.dateHistogram("avg_values_by_interval").field(dateTimeField).minDocCount(1)
				.dateHistogramInterval(DateHistogramInterval.seconds(timeInterval)).subAggregation(directionValues)
				.subAggregation(speedValues));

		return range.subAggregation(AggregationBuilders.count("count").field(defaultField));
		return aggBuilders;
	}

	@SuppressWarnings("unchecked")
@@ -279,12 +98,12 @@ public class WindRoseESRepository extends RTimeSeriesESRepository<TimeSeries, Da
	public QueryBuilder getTermQuery(Map<String, Object> terms, BoolQueryBuilder query) {

		if (terms.containsKey("dataDefinition")) {
			List<Integer> ids = (List<Integer>) (List<?>) terms.get("dataDefinition");
			List<Integer> ids = (List<Integer>) terms.get("dataDefinition");
			query.must(QueryBuilders.boolQuery().filter(QueryBuilders.termsQuery("dataDefinition", ids)));
		}

		if (terms.containsKey("dates")) {
			List<String> dates = (List<String>) (List<?>) terms.get("dates");
			List<String> dates = (List<String>) terms.get("dates");
			query.must(QueryBuilders.boolQuery().filter(QueryBuilders.termsQuery("date", dates)));
		}
		return super.getTermQuery(terms, query);
+34 −21
Original line number Diff line number Diff line
package es.redmic.timeseriesview.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import es.redmic.exception.elasticsearch.ESTermQueryException;
import es.redmic.models.es.common.dto.ElasticSearchDTO;
import es.redmic.models.es.common.query.dto.AggsPropertiesDTO;
import es.redmic.models.es.common.query.dto.DataQueryDTO;
import es.redmic.timeseriesview.dto.windrose.DatesByDirectionListDTO;
import es.redmic.timeseriesview.dto.windrose.WindroseDataDTO;
import es.redmic.timeseriesview.dto.windrose.WindRoseDataDTO;
import es.redmic.timeseriesview.repository.WindRoseESRepository;
import es.redmic.timeseriesview.utils.TimeSeriesUtils;
import es.redmic.viewlib.config.MapperScanBeanItfc;

@Service
@@ -31,9 +35,24 @@ public class WindRoseESService {
		this.repository = repository;
	}

	@SuppressWarnings({ "unchecked" })
	@SuppressWarnings({ "unchecked", "serial" })
	public ElasticSearchDTO getWindRoseData(DataQueryDTO query, String activityId) {

		// Obtiene datos de la query

		Map<String, Object> dataDefinitionMap = (Map<String, Object>) query.getTerms().get("dataDefinition");

		Integer numSectors = (Integer) query.getTerms().get("numSectors"),
				partitionNumber = (Integer) query.getTerms().get("numSplits");

		List<Integer> speedDataDefinition = (List<Integer>) dataDefinitionMap.get("speed"),
				directionDataDefinition = (List<Integer>) dataDefinitionMap.get("direction");

		Long timeIntervalDefault = new Long(query.getTerms().get("timeInterval").toString());

		checkValidNumSectors(numSectors);
		checkValidPartitionNumber(partitionNumber);

		// Añade a query para comprobar que la actividad corresponde con la buscada
		query.setActivityId(activityId);

@@ -43,27 +62,21 @@ public class WindRoseESService {

		query.setQFlags(Arrays.asList(GOOD_QFLAG));

		// Obtiene datos de la query

		Map<String, Object> dataDefinitionMap = (Map<String, Object>) query.getTerms().get("dataDefinition");

		Integer numSectors = (Integer) query.getTerms().get("numSectors"),
				partitionNumber = (Integer) query.getTerms().get("numSplits"),
				speedDataDefinition = (Integer) dataDefinitionMap.get("speed"),
				directionDataDefinition = (Integer) dataDefinitionMap.get("direction");
		query.setSize(0);

		checkValidNumSectors(numSectors);
		checkValidPartitionNumber(partitionNumber);

		Map<String, Object> stats = repository.getStatAggs(query, speedDataDefinition);
		query.getTerms().put("dataDefinition", new ArrayList<Integer>() {
			{
				addAll(speedDataDefinition);
				addAll(directionDataDefinition);
			}
		});

		// Obtener las fechas de los registros de cada uno de los sectores.
		DatesByDirectionListDTO datesByDirectionListDTO = repository.getDatesByDirectionAggs(query, numSectors,
				directionDataDefinition);
		query.addAgg(new AggsPropertiesDTO("dataDefinition", StringUtils.join(speedDataDefinition, ",")));
		query.addAgg(new AggsPropertiesDTO("dataDefinition", StringUtils.join(directionDataDefinition, ",")));
		query.addAgg(new AggsPropertiesDTO("interval",
				TimeSeriesUtils.getTimeInterval(timeIntervalDefault, query.getDateLimits()).toString()));

		// Hacer query para obtener los datos en formato windrose
		WindroseDataDTO windroseDataDTO = repository.getWindroseData(query, datesByDirectionListDTO,
				speedDataDefinition, stats, partitionNumber);
		WindRoseDataDTO windroseDataDTO = repository.getWindRoseData(query, numSectors, partitionNumber);

		return new ElasticSearchDTO(windroseDataDTO, windroseDataDTO.getData().size());
	}