Skip to content

Commit

Permalink
Updated PR after recent changes
Browse files Browse the repository at this point in the history
Road structure has changed a bit since last time this PR was changed, so stuff had to be adjusted.
It is now partially implemented, but at least it's mergeable again. Not entirely sure if API definition is complete like this, think this is the bare minimum.
  • Loading branch information
BertScholten committed Dec 21, 2023
1 parent c1f86a4 commit b1fc6d2
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,209 +14,93 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package nl.overheid.aerius.emissionservice.repository;

import static nl.overheid.aerius.emissionservice.jooq.public_.tables.Substances.SUBSTANCES;
import static nl.overheid.aerius.emissionservice.jooq.template.tables.RoadCategories.ROAD_CATEGORIES;
import static nl.overheid.aerius.emissionservice.jooq.template.tables.RoadCategoriesView.ROAD_CATEGORIES_VIEW;
import static nl.overheid.aerius.emissionservice.jooq.template.tables.RoadSpeedProfiles.ROAD_SPEED_PROFILES;
import static org.jooq.impl.DSL.boolOr;
package nl.aerius.emissionservice.repository;

import static nl.aerius.emissionservice.db.generated.template.tables.I18nRoadAreaCategories.I18N_ROAD_AREA_CATEGORIES;
import static nl.aerius.emissionservice.db.generated.template.tables.I18nRoadTypeCategories.I18N_ROAD_TYPE_CATEGORIES;
import static nl.aerius.emissionservice.db.generated.template.tables.I18nRoadVehicleCategories.I18N_ROAD_VEHICLE_CATEGORIES;
import static nl.aerius.emissionservice.db.generated.template.tables.RoadAreaCategories.ROAD_AREA_CATEGORIES;
import static nl.aerius.emissionservice.db.generated.template.tables.RoadTypeCategories.ROAD_TYPE_CATEGORIES;
import static nl.aerius.emissionservice.db.generated.template.tables.RoadVehicleCategories.ROAD_VEHICLE_CATEGORIES;
import static org.jooq.impl.DSL.coalesce;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.trueCondition;
import static org.jooq.impl.DSL.select;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Result;
import org.springframework.stereotype.Repository;

import nl.overheid.aerius.emissionservice.jooq.public_.enums.RoadType;
import nl.overheid.aerius.emissionservice.jooq.public_.enums.SpeedLimitEnforcementType;
import nl.overheid.aerius.emissionservice.jooq.public_.enums.VehicleType;
import nl.overheid.aerius.emissionservice.model.RoadEmissionFactor;
import nl.overheid.aerius.emissionservice.model.RoadEmissionFactors;
import nl.overheid.aerius.emissionservice.model.RoadSpeedProfileCategory;
import nl.overheid.aerius.emissionservice.model.RoadSpeedProfileCategory.SpeedLimitEnforcementEnum;
import nl.aerius.emissionservice.db.generated.i18n.enums.LanguageCodeType;
import nl.aerius.emissionservice.model.Category;
import nl.aerius.emissionservice.model.RoadEmissionFactors;

@Repository
public class RoadRepository {

private static final Field<Boolean> HAS_SRM_DISTINCTION = field("has_srm_distinction", Boolean.class);

private static final Field<String> SUBSTANCE = field("substance", String.class);
private static final Field<Double> FACTOR = field("factor", Double.class);
private static final Field<Double> STAGNATED_FACTOR = field("stagnated_factor", Double.class);
private static final Field<String> DESCRIPTION = field("description", String.class);
private static final Field<String> I18N_DESCRIPTION = field("i18n_description", String.class);

private final DatasetStore datasetStore;

public RoadRepository(final DatasetStore datasetStore) {
this.datasetStore = datasetStore;
}

public List<String> getVehicleTypes() {
return datasetStore.dsl().selectDistinct(
ROAD_CATEGORIES.VEHICLE_TYPE)
.from(ROAD_CATEGORIES)
.orderBy(ROAD_CATEGORIES.VEHICLE_TYPE)
.fetchInto(String.class);
}

public List<String> getRoadTypes() {
return datasetStore.dsl().selectDistinct(
ROAD_SPEED_PROFILES.ROAD_TYPE)
.from(ROAD_SPEED_PROFILES)
.orderBy(ROAD_SPEED_PROFILES.ROAD_TYPE)
.fetchInto(String.class);
}

public List<RoadSpeedProfileCategory> getRoadSpeedProfiles(final Locale locale, final Optional<String> roadType,
final Optional<String> speedLimitEnforcement, final Optional<Boolean> srm2, final Optional<Boolean> srm1,
final Optional<Integer> maximumSpeed) {
// Separate select to determine which road types have a distinction for SRM1 and SRM2 in profiles.
final Map<RoadType, Boolean> srmDistinction = getSRMDistinctionPerRoadType();

Stream<RoadSpeedProfileCategory> categoriesStream = getRoadSpeedProfileCategories(srmDistinction, roadType, speedLimitEnforcement);
if (srm2.isPresent()) {
categoriesStream = categoriesStream.filter(category -> category.getUsableForSrm2() == srm2.get());
}
if (srm1.isPresent()) {
categoriesStream = categoriesStream.filter(category -> category.getUsableForSrm1() == srm1.get());
}
List<RoadSpeedProfileCategory> results = categoriesStream.collect(Collectors.toList());
if (maximumSpeed.isPresent()) {
results = determineCategoriesForSpeed(maximumSpeed.get(), results);
}
return results;
}

public Optional<RoadEmissionFactors> getEmissionFactors(final Locale locale, final String speedProfile, final String vehicleType,
final Integer year) {
final int speedProfileId;
final VehicleType dbVehicleType;
try {
speedProfileId = Integer.parseInt(speedProfile);
dbVehicleType = VehicleType.valueOf(vehicleType.toLowerCase(locale));
} catch (final IllegalArgumentException e) {
return Optional.empty();
}
final Optional<RoadSpeedProfileCategory> speedProfileCategory = getRoadSpeedProfileCategory(speedProfileId);
return speedProfileCategory.map(category -> new RoadEmissionFactors()
.year(year)
.vehicleType(vehicleType)
.roadSpeedProfileCategory(category)
.emissionFactors(getEmissionFactors(locale, speedProfileId, dbVehicleType, year)));
}

private List<RoadEmissionFactor> getEmissionFactors(final Locale locale, final int speedProfileId, final VehicleType vehicleType,
final Integer year) {
return datasetStore.dsl()
.select(
SUBSTANCES.NAME.as(SUBSTANCE),
ROAD_CATEGORIES_VIEW.EMISSION_FACTOR.as(FACTOR),
ROAD_CATEGORIES_VIEW.STAGNATED_EMISSION_FACTOR.as(STAGNATED_FACTOR))
.from(ROAD_CATEGORIES_VIEW)
.innerJoin(SUBSTANCES).using(ROAD_CATEGORIES_VIEW.SUBSTANCE_ID)
.where(ROAD_CATEGORIES_VIEW.ROAD_SPEED_PROFILE_ID.eq(speedProfileId))
.and(ROAD_CATEGORIES_VIEW.VEHICLE_TYPE.eq(vehicleType))
.and(ROAD_CATEGORIES_VIEW.YEAR.eq((short) year.intValue()))
.fetchInto(RoadEmissionFactor.class);
public List<Category> getRoadAreas(final Locale locale) {
final LanguageCodeType language = DbUtil.getLanguageCodeType(locale);
return datasetStore.dsl().select(
ROAD_AREA_CATEGORIES.CODE,
ROAD_AREA_CATEGORIES.NAME,
coalesce(I18N_DESCRIPTION, ROAD_AREA_CATEGORIES.NAME).as(DESCRIPTION))
.from(ROAD_AREA_CATEGORIES)
.leftJoin(
select(I18N_ROAD_AREA_CATEGORIES.ROAD_AREA_CATEGORY_ID, I18N_ROAD_AREA_CATEGORIES.DESCRIPTION.as(I18N_DESCRIPTION))
.from(I18N_ROAD_AREA_CATEGORIES)
.where(I18N_ROAD_AREA_CATEGORIES.LANGUAGE_CODE.eq(language)))
.using(ROAD_AREA_CATEGORIES.ROAD_AREA_CATEGORY_ID)
.orderBy(ROAD_AREA_CATEGORIES.CODE)
.fetchInto(Category.class);
}

private Map<RoadType, Boolean> getSRMDistinctionPerRoadType() {
public List<Category> getRoadTypes(final Locale locale) {
final LanguageCodeType language = DbUtil.getLanguageCodeType(locale);
return datasetStore.dsl().select(
ROAD_SPEED_PROFILES.ROAD_TYPE,
field(trueCondition()
.and(boolOr(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT.equal(SpeedLimitEnforcementType.irrelevant)))
.and(boolOr(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT.notEqual(SpeedLimitEnforcementType.irrelevant)))).as(HAS_SRM_DISTINCTION))
.from(ROAD_SPEED_PROFILES)
.groupBy(ROAD_SPEED_PROFILES.ROAD_TYPE)
.fetch()
.intoMap(ROAD_SPEED_PROFILES.ROAD_TYPE, HAS_SRM_DISTINCTION);
ROAD_TYPE_CATEGORIES.CODE,
ROAD_TYPE_CATEGORIES.NAME,
coalesce(I18N_DESCRIPTION, ROAD_TYPE_CATEGORIES.NAME).as(DESCRIPTION))
.from(ROAD_TYPE_CATEGORIES)
.leftJoin(
select(I18N_ROAD_TYPE_CATEGORIES.ROAD_TYPE_CATEGORY_ID, I18N_ROAD_TYPE_CATEGORIES.DESCRIPTION.as(I18N_DESCRIPTION))
.from(I18N_ROAD_TYPE_CATEGORIES)
.where(I18N_ROAD_TYPE_CATEGORIES.LANGUAGE_CODE.eq(language)))
.using(ROAD_TYPE_CATEGORIES.ROAD_TYPE_CATEGORY_ID)
.orderBy(ROAD_TYPE_CATEGORIES.CODE)
.fetchInto(Category.class);
}

private Stream<RoadSpeedProfileCategory> getRoadSpeedProfileCategories(final Map<RoadType, Boolean> srmDistinction,
final Optional<String> roadType, final Optional<String> speedLimitEnforcement) {
final Result<Record> results = datasetStore.dsl()
.select()
.from(ROAD_SPEED_PROFILES)
.where(roadType.isPresent()
? ROAD_SPEED_PROFILES.ROAD_TYPE.cast(String.class).eq(roadType.get().toLowerCase())
: trueCondition())
.and(speedLimitEnforcement.isPresent()
? ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT.cast(String.class).eq(speedLimitEnforcement.get().toLowerCase())
: trueCondition())
.fetch();
return results.stream().map(record -> {
final RoadType categoryRoadType = record.get(ROAD_SPEED_PROFILES.ROAD_TYPE);
final boolean usableForSrm1 = !srmDistinction.get(categoryRoadType)
|| record.get(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT) == SpeedLimitEnforcementType.irrelevant;
final boolean usableForSrm2 = !srmDistinction.get(categoryRoadType)
|| record.get(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT) != SpeedLimitEnforcementType.irrelevant;
return new RoadSpeedProfileCategory().code(record.get(ROAD_SPEED_PROFILES.ROAD_SPEED_PROFILE_ID).toString())
.name(record.get(ROAD_SPEED_PROFILES.NAME))
.description(categoryRoadType.name() + ", " + record.get(ROAD_SPEED_PROFILES.NAME))
.roadType(categoryRoadType.name())
.speedLimitEnforcement(SpeedLimitEnforcementEnum.fromValue(record.get(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT).name()))
.usableForSrm1(usableForSrm1)
.usableForSrm2(usableForSrm2)
.maximumSpeed(record.get(ROAD_SPEED_PROFILES.MAXIMUM_SPEED));
});
public List<Category> getVehicleTypes(final Locale locale) {
final LanguageCodeType language = DbUtil.getLanguageCodeType(locale);
return datasetStore.dsl().select(
ROAD_VEHICLE_CATEGORIES.CODE,
ROAD_VEHICLE_CATEGORIES.NAME,
coalesce(I18N_DESCRIPTION, ROAD_VEHICLE_CATEGORIES.NAME).as(DESCRIPTION))
.from(ROAD_VEHICLE_CATEGORIES)
.leftJoin(
select(I18N_ROAD_VEHICLE_CATEGORIES.ROAD_VEHICLE_CATEGORY_ID, I18N_ROAD_VEHICLE_CATEGORIES.DESCRIPTION.as(I18N_DESCRIPTION))
.from(I18N_ROAD_VEHICLE_CATEGORIES)
.where(I18N_ROAD_VEHICLE_CATEGORIES.LANGUAGE_CODE.eq(language)))
.using(ROAD_VEHICLE_CATEGORIES.ROAD_VEHICLE_CATEGORY_ID)
.orderBy(ROAD_VEHICLE_CATEGORIES.CODE)
.fetchInto(Category.class);
}

private List<RoadSpeedProfileCategory> determineCategoriesForSpeed(final int targetMaxSpeed,
final List<RoadSpeedProfileCategory> originalCategories) {
final List<RoadSpeedProfileCategory> categoriesWithMaxSpeed = originalCategories.stream()
.filter(category -> category.getMaximumSpeed() != null)
.collect(Collectors.toList());
//if there are no categories with a maximum speed, just return those.
if (categoriesWithMaxSpeed.isEmpty()) {
return originalCategories;
} else {
final int maxSpeedFound = categoriesWithMaxSpeed.stream()
.mapToInt(category -> category.getMaximumSpeed().intValue())
// Find all speeds that are equal to or above our target speed
.filter(speed -> targetMaxSpeed <= speed)
// Get the minimum of those speeds as the speed that we want to retrieve categories for
.min()
// If none match the criteria (all speeds are below our target speed), use the highest of the speeds
.orElseGet(() -> categoriesWithMaxSpeed.stream()
.mapToInt(category -> category.getMaximumSpeed().intValue())
.max()
.getAsInt());
//Now return all the categories with that maximum speed.
return categoriesWithMaxSpeed.stream()
.filter(category -> category.getMaximumSpeed().intValue() == maxSpeedFound)
.collect(Collectors.toList());
}
public Optional<List<RoadEmissionFactors>> getEmissionFactors(final Locale locale, final String roadArea, final String roadType,
final String vehicleType, final Integer year) {
// TODO: get all emission factors for requested combination
return Optional.empty();
}

private Optional<RoadSpeedProfileCategory> getRoadSpeedProfileCategory(final int speedProfileId) {
final Optional<Record> result = datasetStore.dsl()
.select()
.from(ROAD_SPEED_PROFILES)
.where(ROAD_SPEED_PROFILES.ROAD_SPEED_PROFILE_ID.eq(speedProfileId))
.fetchOptional();
final Map<RoadType, Boolean> srmDistinction = getSRMDistinctionPerRoadType();
return result.map(record -> {
final RoadType categoryRoadType = record.get(ROAD_SPEED_PROFILES.ROAD_TYPE);
final boolean usableForSrm1 = !srmDistinction.get(categoryRoadType)
|| record.get(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT) == SpeedLimitEnforcementType.irrelevant;
final boolean usableForSrm2 = !srmDistinction.get(categoryRoadType)
|| record.get(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT) != SpeedLimitEnforcementType.irrelevant;
return new RoadSpeedProfileCategory().code(record.get(ROAD_SPEED_PROFILES.ROAD_SPEED_PROFILE_ID).toString())
.name(record.get(ROAD_SPEED_PROFILES.NAME))
.description(categoryRoadType.name() + ", " + record.get(ROAD_SPEED_PROFILES.NAME))
.roadType(categoryRoadType.name())
.speedLimitEnforcement(SpeedLimitEnforcementEnum.fromValue(record.get(ROAD_SPEED_PROFILES.SPEED_LIMIT_ENFORCEMENT).name()))
.usableForSrm1(usableForSrm1)
.usableForSrm2(usableForSrm2)
.maximumSpeed(record.get(ROAD_SPEED_PROFILES.MAXIMUM_SPEED));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.Locale;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
Expand All @@ -37,10 +36,12 @@
import nl.aerius.emissionservice.model.FarmFodderMeasureCategory;
import nl.aerius.emissionservice.model.FarmLodgingCategory;
import nl.aerius.emissionservice.model.FarmReductiveLodgingSystemCategory;
import nl.aerius.emissionservice.model.RoadEmissionFactors;
import nl.aerius.emissionservice.model.Sector;
import nl.aerius.emissionservice.repository.DatasetStore;
import nl.aerius.emissionservice.repository.FarmRepository;
import nl.aerius.emissionservice.repository.FarmlandRepository;
import nl.aerius.emissionservice.repository.RoadRepository;
import nl.aerius.emissionservice.repository.SectorRepository;

@Service
Expand Down Expand Up @@ -160,36 +161,27 @@ public ResponseEntity<List<Category>> listFarmlands(final String dataset, final
}

@Override
public ResponseEntity<List<String>> listVehicleTypes(final String dataset, final Optional<String> acceptLanguage) {
return handle(dataset, acceptLanguage, roadRepository::getVehicleTypes);
public ResponseEntity<List<Category>> listRoadAreas(final String dataset, final Optional<String> acceptLanguage) {
return handle(dataset, acceptLanguage, roadRepository::getRoadAreas);
}

@Override
public ResponseEntity<List<String>> listRoadTypes(final String dataset, final Optional<String> acceptLanguage) {
public ResponseEntity<List<Category>> listRoadTypes(final String dataset, final Optional<String> acceptLanguage) {
return handle(dataset, acceptLanguage, roadRepository::getRoadTypes);
}

@Override
public ResponseEntity<List<RoadSpeedProfileCategory>> listSpeedProfiles(final String dataset, final Optional<String> acceptLanguage,
final Optional<String> roadtype, final Optional<String> speedlimitenforcement, final Optional<Boolean> srm2,
final Optional<Boolean> srm1, final Optional<Integer> maximumspeed) {
return handle(dataset, acceptLanguage,
locale -> roadRepository.getRoadSpeedProfiles(locale, roadtype, speedlimitenforcement, srm2, srm1, maximumspeed));
public ResponseEntity<List<Category>> listVehicleTypes(final String dataset, final Optional<String> acceptLanguage) {
return handle(dataset, acceptLanguage, roadRepository::getVehicleTypes);
}

@Override
public ResponseEntity<RoadEmissionFactors> getRoadEmissionFactors(final String dataset, final String speedprofile, final String vehicletype,
final Integer year, final Optional<String> acceptLanguage) {
return handle(dataset, acceptLanguage, locale -> roadRepository.getEmissionFactors(locale, speedprofile, vehicletype, year).orElseThrow(
public ResponseEntity<List<RoadEmissionFactors>> getRoadEmissionFactors(final String dataset, final String roadarea, final String roadtype,
final String vehicletype, final Integer year, final Optional<String> acceptLanguage) {
return handle(dataset, acceptLanguage, locale -> roadRepository.getEmissionFactors(locale, roadarea, roadtype, vehicletype, year).orElseThrow(
() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Could not find road emission factors for supplied parameters")));
}

private <T> ResponseEntity<T> handle(final String dataset, final Optional<String> acceptLanguage, final Supplier<T> function) {
final String actualDataset = handleDataset(dataset);
final T result = function.get();
return toOkResponse(actualDataset, result);
}

private <T> ResponseEntity<T> handle(final String dataset, final Optional<String> acceptLanguage, final Function<Locale, T> function) {
final String actualDataset = handleDataset(dataset);
final Locale locale = localeHelper.getResponseLocale(acceptLanguage);
Expand Down
Loading

0 comments on commit b1fc6d2

Please sign in to comment.