class SearchByLocationCommand implements CommandHasRequest<SearchByLocationRequest, SearchByLocationResponse> {

	@Override
	public void execute(SearchByLocationRequest request, SearchByLocationResponse response) throws Exception {
		if (null == request.getLayerIds()) {
			throw new GeomajasException(ExceptionCode.PARAMETER_MISSING, "layerIds");
		}
		String crsCode = request.getCrs();
		if (null == crsCode) {
			throw new GeomajasException(ExceptionCode.PARAMETER_MISSING, "crs");
		}
		Geometry location = converter.toInternal(request.getLocation());
		int queryType = request.getQueryType();
		double ratio = request.getRatio();
		int searchType = request.getSearchType();
		Crs crs = geoService.getCrs2(request.getCrs());

		// Check if a buffer should be added around the location:
		Geometry geometry = location;
		if (request.getBuffer() > 0) {
			geometry = location.buffer(request.getBuffer());
		}
		log.debug("search by location " + geometry);

		for (String clientLayerId : request.getLayerIds()) {
			String serverLayerId = request.getServerLayerId(clientLayerId);
			if (null == serverLayerId) {
				throw new GeomajasException(ExceptionCode.PARAMETER_MISSING, 
								"serverLayerId for clientLayerId " + clientLayerId);
			}
			if (securityContext.isLayerVisible(serverLayerId)) {
				VectorLayer vectorLayer = configurationService.getVectorLayer(serverLayerId);
				if (vectorLayer != null) {
					String geomName = vectorLayer.getLayerInfo().getFeatureInfo().getGeometryType().getName();

					// Transform geometry to layer CRS:
					Geometry layerGeometry = geoService.transform(geometry, crs, layerService.getCrs(vectorLayer));
					log.trace("on layer " + serverLayerId + " use " + layerGeometry);

					// Create the correct Filter object:
					Filter f = null;
					switch (queryType) {
						case SearchByLocationRequest.QUERY_INTERSECTS:
							f = filterCreator.createIntersectsFilter(layerGeometry, geomName);
							break;
						case SearchByLocationRequest.QUERY_CONTAINS:
							f = filterCreator.createContainsFilter(layerGeometry, geomName);
							break;
						case SearchByLocationRequest.QUERY_TOUCHES:
							f = filterCreator.createTouchesFilter(layerGeometry, geomName);
							break;
						case SearchByLocationRequest.QUERY_WITHIN:
							f = filterCreator.createWithinFilter(layerGeometry, geomName);
							break;
						default:
							throw new IllegalArgumentException("Unknown query type " + queryType);
					}
					//Set the per layer filter
					if (null != request.getFilter(clientLayerId)) {
						if (null == f) {
							f = filterCreator.parseFilter(request.getFilter(clientLayerId));
						} else {
							f = filterCreator.createAndFilter(
									filterCreator.parseFilter(request.getFilter(clientLayerId)), f);
						}
					}
					//Set the global filter
					if (null != request.getFilter()) {
						if (null == f) {
							f = filterCreator.parseFilter(request.getFilter());
						} else {
							f = filterCreator.createAndFilter(filterCreator.parseFilter(request.getFilter()), f);
						}
					}

					// Get the features:
					List<InternalFeature> temp = layerService.getFeatures(serverLayerId, crs, f, null, request
							.getFeatureIncludes());
					if (temp.size() > 0) {
						List<Feature> features = new ArrayList<Feature>();

						// Calculate overlap ratio in case of intersects:
						if (queryType == SearchByLocationRequest.QUERY_INTERSECTS && ratio >= 0 && ratio < 1) {
							for (InternalFeature feature : temp) {
								double minimalOverlap = feature.getGeometry().getArea() * ratio;
								Geometry overlap = geometry.intersection(feature.getGeometry());
								double effectiveOverlap = overlap.getArea();
								if (minimalOverlap <= effectiveOverlap) {
									log.trace("found " + feature);
									Feature dto = converter.toDto(feature);
									dto.setCrs(crsCode);
									features.add(dto);
								}
							}
						} else {
							for (InternalFeature feature : temp) {
								log.trace("found " + feature);
								Feature dto = converter.toDto(feature);
								dto.setCrs(crsCode);
								features.add(dto);
							}
						}

						// features.size can again be 0... so check:
						if (features.size() > 0) {
							// We have a response for this layer!
							response.addLayer(clientLayerId, features);

							// If searchType == SEARCH_FIRST_LAYER, we should search no further:
							if (searchType == SearchByLocationRequest.SEARCH_FIRST_LAYER) {
								break;
							}
						}
					}
				}
			}
		}
	}

}