Commit 238b68b1 authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck
Browse files

add JoltTransformation class to allow postTransformation hooks.

This addition is needed because not all transformation can be done
with the Jolt Transformation. Each implementation of JoltTransformation
has a list of PostTransformHooks which will be executed in order on
the JsonNode representation _after_ the Jolt transformation is done.

This allows context specific transformation. If needed a postHook will
be added in the future.
parent dec1cf87
package org.vadere.simulator.projects.migration.jolttranformation;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.vadere.simulator.entrypoints.Version;
import org.vadere.simulator.models.gnm.GradientNavigationModel;
import org.vadere.simulator.models.osm.OptimalStepsModel;
import org.vadere.simulator.models.potential.PotentialFieldObstacleCompact;
import org.vadere.simulator.models.potential.PotentialFieldObstacleOSM;
import org.vadere.simulator.models.potential.PotentialFieldPedestrianCompact;
import org.vadere.simulator.models.potential.PotentialFieldPedestrianOSM;
import org.vadere.simulator.models.sfm.SocialForceModel;
import org.vadere.simulator.projects.migration.MigrationException;
import org.vadere.state.attributes.models.AttributesGNM;
import org.vadere.state.attributes.models.AttributesOSM;
import org.vadere.state.attributes.models.AttributesPotentialCompact;
import org.vadere.state.attributes.models.AttributesPotentialOSM;
import org.vadere.state.attributes.scenario.AttributesSource;
import java.util.Iterator;
public class JoltTransformV0toV1 extends JoltTransformation {
JoltTransformV0toV1(String transformation, String identity, Version version) {
super(transformation, identity, version);
}
@Override
protected void initPostHooks() {
postTransformHooks.add(this::findMainModel);
postTransformHooks.add(this::attributesPotentialCompactVSosmIncident);
postTransformHooks.add(this::moveSpawnDelayIntoDistributionParametersIncident);
}
private JsonNode findMainModel(JsonNode node) throws MigrationException {
JsonNode scenario = node.path("scenario");
if (scenario.isMissingNode()){
logger.error("There must be scenario Node");
throw new MigrationException("There must be scenario Node");
}
// Possible Main Models
String mainModelValue = "";
if (! node.path("scenario").path("attributesModel").path(AttributesOSM.class.getName()).isMissingNode()){
mainModelValue = OptimalStepsModel.class.getName();
}
if (! node.path("scenario").path("attributesModel").path(AttributesGNM.class.getName()).isMissingNode()){
if (!mainModelValue.equals("")){
throw new MigrationException("can't automatically determine the mainModel - more than one mainModel-suitable model is present");
}
mainModelValue = GradientNavigationModel.class.getName();
}
if (! node.path("scenario").path("attributesModel").path(AttributesGNM.class.getName()).isMissingNode()){
if (!mainModelValue.equals("")){
throw new MigrationException("can't automatically determine the mainModel - more than one mainModel-suitable model is present");
}
mainModelValue = SocialForceModel.class.getName();
}
if (mainModelValue.equals("")){
throw new MigrationException("couldn't automatically determine the mainModel based on the present models OSM, GNM, SFM");
}
addToObjectNode(scenario, "mainModel", mainModelValue);
return node;
}
/**
* Set specific fields within the AttributesOSM depending on other present Attributes. This
* Transformation is context sensitive thus handle this with a hook and not within the
* Jolt transformation
*/
private JsonNode attributesPotentialCompactVSosmIncident (JsonNode node) throws MigrationException {
JsonNode osmAttr = node.path("scenario").path("attributesModel").path(AttributesOSM.class.getCanonicalName());
if (osmAttr.isMissingNode())
return node;
JsonNode potentialCompactAttr = node.path("scenario").path("attributesModel").path(AttributesPotentialCompact.class.getCanonicalName());
JsonNode potentialOSMAttr = node.path("scenario").path("attributesModel").path(AttributesPotentialOSM.class.getCanonicalName());
if (!potentialCompactAttr.isMissingNode() && !potentialOSMAttr.isMissingNode()){
throw new MigrationException("[AttributesPotentialCompact] and [AttributesPotentialOSM] are both present, that is not allowed.");
}
String beforeChange = osmAttr.toString();
if (!potentialCompactAttr.isMissingNode()){
addToObjectNode(osmAttr, "pedestrianPotentialModel", PotentialFieldPedestrianCompact.class.getName());
addToObjectNode(osmAttr, "obstaclePotentialModel", PotentialFieldObstacleCompact.class.getName());
if (!beforeChange.equals(osmAttr.toString())) {
logger.info("\t- AttributesOSM: since AttributesPotentialCompact is present, set [pedestrianPotentialModel] to PotentialFieldPedestrianCompact " +
"and [obstaclePotentialModel] to PotentialFieldObstacleCompact" + "\n");
}
}
if (!potentialOSMAttr.isMissingNode()){
addToObjectNode(osmAttr, "pedestrianPotentialModel", PotentialFieldPedestrianOSM.class.getName());
addToObjectNode(osmAttr, "obstaclePotentialModel", PotentialFieldObstacleOSM.class.getName());
if (!beforeChange.equals(osmAttr.toString())) {
logger.info("\t- AttributesOSM: since AttributesPotentialOSM is present, set [pedestrianPotentialModel] to PotentialFieldPedestrianOSM " +
"and [obstaclePotentialModel] to PotentialFieldObstacleOSM" + "\n");
}
}
return node;
}
/**
* If the interSpawnTimeDistribution is set within one source rebuild the distributionParameters
*/
private JsonNode moveSpawnDelayIntoDistributionParametersIncident (JsonNode node) {
JsonNode sources = node.path("scenario").path("topography").path("sources");
if (sources.isMissingNode())
return node;
Iterator<JsonNode> iter = sources.elements();
//apply for reach source
while (iter.hasNext()){
JsonNode source = iter.next();
final double spawnDelay = source.path("spawnDelay").asDouble();
JsonNode distribution = source.path("interSpawnTimeDistribution");
if (spawnDelay != -1.0 && (!distribution.isMissingNode() ||
distribution.asText().equals(AttributesSource.CONSTANT_DISTRIBUTION))){
setDoubelArray(source, "distributionParameters", spawnDelay);
}
((ObjectNode)source).remove("spawnDelay");
}
return node;
}
}
package org.vadere.simulator.projects.migration.jolttranformation;
import org.vadere.simulator.entrypoints.Version;
public class JoltTransformV1toV2 extends JoltTransformation {
public JoltTransformV1toV2(String transformation, String identity, Version version) {
super(transformation, identity, version);
}
@Override
protected void initPostHooks() {
}
}
package org.vadere.simulator.projects.migration.jolttranformation;
import org.vadere.simulator.entrypoints.Version;
public class JoltTransformV2toV3 extends JoltTransformation {
public JoltTransformV2toV3(String transformation, String identity, Version version) {
super(transformation, identity, version);
}
@Override
protected void initPostHooks() {
}
}
package org.vadere.simulator.projects.migration.jolttranformation;
import com.bazaarvoice.jolt.Chainr;
import com.bazaarvoice.jolt.Diffy;
import com.bazaarvoice.jolt.JsonUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.log4j.Logger;
import org.vadere.simulator.entrypoints.Version;
import org.vadere.simulator.projects.migration.MigrationException;
import org.vadere.simulator.projects.migration.incidents.VersionBumpIncident;
import org.vadere.state.util.StateJsonConverter;
import java.util.ArrayList;
public abstract class JoltTransformation {
protected final static Logger logger = Logger.getLogger(JoltTransformation.class);
private final Chainr chainr; // Transformation from (version -1) to version
private final Chainr identity; // Identity of version
private final Version version; // Output of Transformation
private final Diffy diffy;
protected ArrayList<PostTransformHook> postTransformHooks;
public static JoltTransformation get(Version v) throws MigrationException {
if (v.equalOrSamller(Version.UNDEFINED))
throw new MigrationException("There is now Transformation for Version " + Version.UNDEFINED.toString());
if (v.equals(Version.latest()))
throw new MigrationException("No Transformation needed. Already latest Version!");
String transformationResource = "transform_v" + v.label('-').toUpperCase() + "_to_v" + v.nextVersion().label('-').toUpperCase() + ".json";
String identityResource = "identity_v" + v.nextVersion().label('-').toUpperCase() + ".json";
JoltTransformation ret = null;
switch (v) {
case NOT_A_RELEASE:
ret = new JoltTransformV0toV1(transformationResource, identityResource, v);
break;
case V0_1:
ret = new JoltTransformV1toV2(transformationResource, identityResource, v);
break;
case V0_2:
ret = new JoltTransformV2toV3(transformationResource, identityResource, v);
break;
}
if (ret == null)
throw new MigrationException("No Transformation defined for Verson " + v.toString());
return ret;
}
public JoltTransformation(String transformation, String identity, Version version){
this.chainr = Chainr.fromSpec(JsonUtils.classpathToList(transformation));
this.identity = Chainr.fromSpec(JsonUtils.classpathToList(identity));
this.version = version;
this.postTransformHooks = new ArrayList<>();
this.diffy = new Diffy();
initPostHooks();
}
public JsonNode applyTransformation(JsonNode root) throws MigrationException {
Object rootObject = StateJsonConverter.convertJsonNodeToObject(root);
rootObject = applyTransformation(rootObject);
JsonNode jsonRoot = StateJsonConverter.deserializeToNode(rootObject);
return applyPostHooks(jsonRoot);
}
private Object applyTransformation(Object root) throws MigrationException {
Object rootTransformed = chainr.transform(root);
Object rootTransformedIdenity = identity.transform(rootTransformed);
Diffy.Result diffResult = diffy.diff(rootTransformed, rootTransformedIdenity);
if (diffResult.isEmpty()){
return rootTransformed;
} else {
logger.error("Error in Transformation " + diffResult.toString());
throw new MigrationException("Error in Transformation " + diffResult.toString());
}
}
private JsonNode applyPostHooks(JsonNode root) throws MigrationException{
JsonNode ret = root;
for (PostTransformHook hook : postTransformHooks) {
ret = hook.applyHook(ret);
}
return ret;
}
/**
* add PostHooks in the correct order.
*/
protected abstract void initPostHooks();
protected void addToObjectNode(JsonNode node, String key, String value){
((ObjectNode)node).put(key, value);
}
protected void setDoubelArray(JsonNode node, String key, Double value){
((ObjectNode)node).set(key, StateJsonConverter.toJsonNode(new Double[] {value}));
}
public ArrayList<PostTransformHook> getPostTransformHooks() {
return postTransformHooks;
}
public void setPostTransformHooks(ArrayList<PostTransformHook> postTransformHooks) {
this.postTransformHooks = postTransformHooks;
}
}
package org.vadere.simulator.projects.migration.jolttranformation;
import com.fasterxml.jackson.databind.JsonNode;
import org.vadere.simulator.projects.migration.MigrationException;
@FunctionalInterface
public interface PostTransformHook {
JsonNode applyHook (JsonNode root) throws MigrationException;
}
{
"topographyhash": "4cd0fdc685fc51e4ae57d0514b3d325d4f2e1340",
"name": "Test1",
"processWriters": [],
"attributeshash": "af732ad440af7f01cd0cc12608657058fd55f8a8",
"vadere": {
"attributesModel": {
"OPTIMAL_STEPS_MODEL": {
"stepCircleResolution": 18.0,
"numberOfCircles": 1.0,
"varyStepDirection": false,
"stepLengthIntercept": 0.4625,
"stepLengthSlopeSpeed": 0.2345,
"stepLengthSD": 0.036,
"movementThreshold": 0.0,
"optimizationType": "DISCRETE",
"movementType": "ARBITRARY",
"dynamicStepLength": false,
"updateType": "EVENT_DRIVEN",
"seeSmallWalls": false
},
"FLOORFIELD": {
"createMethod": "HIGH_ACCURACY_FAST_MARCHING",
"potentialFieldResolution": 0.1,
"obstacleGridPenalty": 0.1,
"targetAttractionStrength": 1.0,
"timeCostAttributes": {
"standardDerivation": 0.7,
"pedestrianTorso": 0.4,
"meanSpeed": 1.34,
"type": "UNIT",
"obstacleDensityWeight": 3.5,
"pedestrianSameTargetDensityWeight": 3.5,
"pedestrianOtherTargetDensityWeight": 3.5,
"pedestrianWeight": 3.5,
"queueWidthLoading": 1.0,
"pedestrianDynamicWeight": 6.0,
"loadingType": "CONSTANT"
}
},
"POTENTIAL_COMPACT_SUPPORT": {
"pedPotentialWidth": 0.5,
"pedPotentialHeight": 12.6,
"obstPotentialWidth": 0.25,
"obstPotentialHeight": 20.1,
"visionFieldAngle": 1.8849555921538759,
"visionFieldRadius": 5.0,
"numPedConsidered": 5.0
}
},
"attributesPedestrian": {
"radius": 0.195,
"densityDependentSpeed": false,
"speedDistributionMean": 1.34,
"speedDistributionStandardDeviation": 0.0,
"minimumSpeed": 0.3,
"maximumSpeed": 3.0,
"acceleration": 2.0,
"id": -1
},
"attributesSimulation": {
"simTimeStepLength": 0.4,
"realTimeSimTimeRatio": 0.1,
"writeSimulationData": true,
"visualizationEnabled": true,
"printFPS": false,
"needsBoundary": false,
"digitsPerCoordinate": 2,
"useRandomSeed": true,
"randomSeed": 1
},
"topography": {
"attributes": {
"finishTime": 60.0,
"bounds": {
"x": 0.0,
"y": 0.0,
"width": 10.0,
"height": 10.0
},
"boundingBoxWidth": 0.5,
"bounded": true
},
"obstacles": [
{
"shape": {
"x": 3.9,
"y": 8.0,
"width": 2.2,
"height": 1.1,
"type": "RECTANGLE"
},
"id": -1
}
],
"stairs": [],
"targets": [
{
"id": 1,
"absorbing": true,
"shape": {
"x": 8.0,
"y": 1.0,
"width": 1.0,
"height": 1.0,
"type": "RECTANGLE"
},
"waitingTime": 0.0,
"parallelWaiters": 0
}
],
"sources": [
{
"id": 2,
"shape": {
"x": 1.0,
"y": 8.0,
"width": 1.0,
"height": 1.0,
"type": "RECTANGLE"
},
"spawnDelay": 1.0,
"spawnNumber": 1,
"startTime": 0.0,
"endTime": 40.0,
"spawnAtRandomPositions": false,
"useFreeSpaceOnly": false,
"targetIds": [
1
]
}
],
"dynamicElements": [],
"pedestrians": []
}
},
"commithash": "c45f1d26328baaec22418c15e6351a059e910f5f"
}
\ No newline at end of file
{
"topographyhash": "0f0df2931e5781bb76327a62305844c0b7f84f5c",
"name": "Test2",
"processWriters": [],
"attributeshash": "af732ad440af7f01cd0cc12608657058fd55f8a8",
"vadere": {
"attributesModel": {
"OPTIMAL_STEPS_MODEL": {
"stepCircleResolution": 18.0,
"numberOfCircles": 1.0,
"varyStepDirection": false,
"stepLengthIntercept": 0.4625,
"stepLengthSlopeSpeed": 0.2345,
"stepLengthSD": 0.036,
"movementThreshold": 0.0,
"optimizationType": "DISCRETE",
"movementType": "ARBITRARY",
"dynamicStepLength": false,
"updateType": "EVENT_DRIVEN",
"seeSmallWalls": false
},
"FLOORFIELD": {
"createMethod": "HIGH_ACCURACY_FAST_MARCHING",
"potentialFieldResolution": 0.1,
"obstacleGridPenalty": 0.1,
"targetAttractionStrength": 1.0,
"timeCostAttributes": {
"standardDerivation": 0.7,
"pedestrianTorso": 0.4,
"meanSpeed": 1.34,
"type": "UNIT",
"obstacleDensityWeight": 3.5,
"pedestrianSameTargetDensityWeight": 3.5,
"pedestrianOtherTargetDensityWeight": 3.5,
"pedestrianWeight": 3.5,
"queueWidthLoading": 1.0,
"pedestrianDynamicWeight": 6.0,
"loadingType": "CONSTANT"
}
},
"POTENTIAL_COMPACT_SUPPORT": {
"pedPotentialWidth": 0.5,
"pedPotentialHeight": 12.6,
"obstPotentialWidth": 0.25,
"obstPotentialHeight": 20.1,
"visionFieldAngle": 1.8849555921538759,
"visionFieldRadius": 5.0,
"numPedConsidered": 5.0
}
},
"attributesPedestrian": {
"radius": 0.195,
"densityDependentSpeed": false,
"speedDistributionMean": 1.34,
"speedDistributionStandardDeviation": 0.0,
"minimumSpeed": 0.3,
"maximumSpeed": 3.0,
"acceleration": 2.0,
"id": -1
},
"attributesSimulation": {
"simTimeStepLength": 0.4,
"realTimeSimTimeRatio": 0.1,
"writeSimulationData": true,
"visualizationEnabled": true,
"printFPS": false,
"needsBoundary": false,
"digitsPerCoordinate": 2,
"useRandomSeed": true,
"randomSeed": 1
},
"topography": {
"attributes": {
"finishTime": 500.0,
"bounds": {
"x": 0.0,
"y": 0.0,
"width": 10.0,
"height": 10.0
},
"boundingBoxWidth": 0.5,
"bounded": true
},
"obstacles": [
{
"shape": {
"x": 3.1,
"y": 7.4,
"width": 2.4,
"height": 1.4,
"type": "RECTANGLE"
},
"id": -1
}
],
"stairs": [],
"targets": [],
"sources": [
{
"id": 2,
"shape": {
"x": 1.0,
"y": 8.0,
"width": 1.0,
"height": 1.0,
"type": "RECTANGLE"
},
"spawnDelay": 1.0,
"interSpawnTimeDistribution" : "org.vadere.state.scenario.ConstantDistribution",
"spawnNumber": 1,
"startTime": 0.0,
"endTime": 40.0,
"spawnAtRandomPositions": false,
"useFreeSpaceOnly": false,
"targetIds": [
1
]
}
],
"dynamicElements": [],
"pedestrians": []
}
},
"commithash": "c45f1d26328baaec22418c15e6351a059e910f5f"
}
\ No newline at end of file
{
"topographyhash": "0f0df2931e5781bb76327a62305844c0b7f84f5c",
"name": "Test2",
"processWriters": [],
"attributeshash": "af732ad440af7f01cd0cc12608657058fd55f8a8",
"vadere": {
"attributesModel": {
"FLOORFIELD": {
"createMethod": "HIGH_ACCURACY_FAST_MARCHING",
"potentialFieldResolution": 0.1,
"obstacleGridPenalty": 0.1,
"targetAttractionStrength": 1.0,
"timeCostAttributes": {
"standardDerivation": 0.7,
"pedestrianTorso": 0.4,
"meanSpeed": 1.34,
"type": "UNIT",
"obstacleDensityWeight": 3.5,
"pedestrianSameTargetDensityWeight": 3.5,
"pedestrianOtherTargetDensityWeight": 3.5,
"pedestrianWeight": 3.5,
"queueWidthLoading": 1.0,
"pedestrianDynamicWeight": 6.0,
"loadingType": "CONSTANT"
}
},
"POTENTIAL_COMPACT_SUPPORT": {