diff --git a/data/presets.yaml b/data/presets.yaml index ab5770551..f6b2138d0 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -583,6 +583,9 @@ en: emergency: # emergency=* label: Emergency + enforcement: + # enforcement=* + label: Type entrance: # entrance=* label: Type @@ -6647,6 +6650,10 @@ en: # 'type=boundary, boundary=administrative' name: Administrative Boundary terms: '' + type/enforcement: + # type=enforcement + name: Enforcement + terms: '' type/multipolygon: # type=multipolygon name: Multipolygon diff --git a/data/presets/README.md b/data/presets/README.md index cecdbfa5d..7bb11ee19 100644 --- a/data/presets/README.md +++ b/data/presets/README.md @@ -233,6 +233,26 @@ For number fields, the lowest valid value. There is no default. For number fields, the greatest valid value. There is no default. +##### `prerequisiteTag` + +An object defining the tags the feature needs before this field will be displayed. It must have this property: + +- `key`: The key for the required tag. + +And may optionally have one of these properties: + +- `value`: The value that the key must have. +- `valueNot`: The value that the key must not have. + +For example, this is how we show the Internet Access Fee field only if the feature has an `internet_access` tag not equal to `no`. + +```js +"prerequisiteTag": { + "key": "internet_access", + "valueNot": "no" +} +``` + ## Icons You can use any of the following open source map icon sets as preset icons. diff --git a/data/presets/defaults.json b/data/presets/defaults.json index a6db43d57..71247c4da 100644 --- a/data/presets/defaults.json +++ b/data/presets/defaults.json @@ -54,6 +54,7 @@ "type/boundary", "type/waterway", "type/multipolygon", + "type/enforcement", "type/site", "relation" ] diff --git a/data/presets/fields.json b/data/presets/fields.json index 6c10c0cbf..ef9748a48 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -77,7 +77,7 @@ "dance/style": {"key": "dance:style", "type": "semiCombo", "label": "Dance Styles"}, "date": {"key": "date", "type": "check", "label": "Date"}, "delivery": {"key": "delivery", "type": "check", "label": "Delivery"}, - "denomination": {"key": "denomination", "type": "combo", "label": "Denomination"}, + "denomination": {"key": "denomination", "type": "combo", "label": "Denomination", "prerequisiteTag": {"key": "religion"}}, "denotation": {"key": "denotation", "type": "combo", "label": "Denotation"}, "description": {"key": "description", "type": "textarea", "label": "Description", "universal": true}, "design": {"key": "design", "type": "combo", "label": "Design"}, @@ -99,6 +99,7 @@ "email": {"key": "email", "type": "email", "placeholder": "example@example.com", "universal": true, "label": "Email"}, "embankment": {"key": "embankment", "type": "typeCombo", "label": "Type", "placeholder": "Default"}, "emergency": {"key": "emergency", "type": "check", "label": "Emergency"}, + "enforcement": {"key": "enforcement", "type": "combo", "label": "Type"}, "entrance": {"key": "entrance", "type": "typeCombo", "label": "Type"}, "except": {"key": "except", "type": "combo", "label": "Exceptions"}, "faces": {"key": "faces", "type": "number", "minValue": 0, "label": "Faces"}, @@ -153,8 +154,8 @@ "intermittent_yes": {"key": "intermittent", "type": "check", "label": "Intermittent", "default": "yes"}, "intermittent": {"key": "intermittent", "type": "check", "label": "Intermittent"}, "internet_access": {"key": "internet_access", "type": "combo", "label": "Internet Access", "strings": {"options": {"yes": "Yes", "no": "No", "wlan": "Wifi", "wired": "Wired", "terminal": "Terminal"}}}, - "internet_access/fee": {"key": "internet_access:fee", "type": "check", "label": "Internet Access Fee"}, - "internet_access/ssid": {"key": "internet_access:ssid", "type": "text", "label": "SSID (Network Name)"}, + "internet_access/fee": {"key": "internet_access:fee", "type": "check", "label": "Internet Access Fee", "prerequisiteTag": {"key": "internet_access", "valueNot": "no"}}, + "internet_access/ssid": {"key": "internet_access:ssid", "type": "text", "label": "SSID (Network Name)", "prerequisiteTag": {"key": "internet_access", "valueNot": "no"}}, "kerb": {"key": "kerb", "type": "combo", "label": "Curb"}, "label": {"key": "label", "type": "textarea", "label": "Label"}, "lamp_type": {"key": "lamp_type", "type": "combo", "label": "Type"}, diff --git a/data/presets/fields/denomination.json b/data/presets/fields/denomination.json index a4a24ca9d..71d664d98 100644 --- a/data/presets/fields/denomination.json +++ b/data/presets/fields/denomination.json @@ -1,5 +1,8 @@ { "key": "denomination", "type": "combo", - "label": "Denomination" -} \ No newline at end of file + "label": "Denomination", + "prerequisiteTag": { + "key": "religion" + } +} diff --git a/data/presets/fields/enforcement.json b/data/presets/fields/enforcement.json new file mode 100644 index 000000000..e330ae569 --- /dev/null +++ b/data/presets/fields/enforcement.json @@ -0,0 +1,5 @@ +{ + "key": "enforcement", + "type": "combo", + "label": "Type" +} diff --git a/data/presets/fields/internet_access/fee.json b/data/presets/fields/internet_access/fee.json index 9b2e407d4..2ca48b4ba 100644 --- a/data/presets/fields/internet_access/fee.json +++ b/data/presets/fields/internet_access/fee.json @@ -1,5 +1,9 @@ { "key": "internet_access:fee", "type": "check", - "label": "Internet Access Fee" + "label": "Internet Access Fee", + "prerequisiteTag": { + "key": "internet_access", + "valueNot": "no" + } } diff --git a/data/presets/fields/internet_access/ssid.json b/data/presets/fields/internet_access/ssid.json index 5f6b4b56f..2309c2b38 100644 --- a/data/presets/fields/internet_access/ssid.json +++ b/data/presets/fields/internet_access/ssid.json @@ -1,5 +1,9 @@ { "key": "internet_access:ssid", "type": "text", - "label": "SSID (Network Name)" + "label": "SSID (Network Name)", + "prerequisiteTag": { + "key": "internet_access", + "valueNot": "no" + } } diff --git a/data/presets/presets.json b/data/presets/presets.json index 4fd66887b..a46eff7b5 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -786,7 +786,7 @@ "public_transport/station_tram": {"icon": "temaki-tram", "fields": ["name", "network", "operator", "address", "building_area", "internet_access", "internet_access/fee", "internet_access/ssid"], "geometry": ["point", "area"], "tags": {"public_transport": "station", "tram": "yes"}, "reference": {"key": "public_transport", "value": "station"}, "terms": ["electric", "light rail", "public transit", "public transportation", "rail", "station", "streetcar", "terminal", "track", "tram", "trolley", "transit", "transportation"], "name": "Tram Station"}, "public_transport/station_trolleybus": {"icon": "temaki-trolleybus", "fields": ["name", "network", "operator", "address", "building_area", "internet_access", "internet_access/fee", "internet_access/ssid"], "geometry": ["point", "area"], "tags": {"public_transport": "station", "trolleybus": "yes"}, "addTags": {"public_transport": "station", "trolleybus": "yes", "amenity": "bus_station"}, "removeTags": {"public_transport": "station", "trolleybus": "yes", "amenity": "bus_station"}, "reference": {"key": "amenity", "value": "bus_station"}, "terms": ["bus", "electric", "public transit", "public transportation", "station", "streetcar", "terminal", "trackless", "tram", "trolley", "transit", "transportation"], "name": "Trolleybus Station / Terminal"}, "public_transport/station": {"icon": "maki-rail", "fields": ["name", "network", "operator", "address", "building_area", "internet_access", "internet_access/fee", "internet_access/ssid"], "geometry": ["point", "area"], "tags": {"public_transport": "station"}, "terms": ["public transit", "public transportation", "station", "terminal", "transit", "transportation"], "name": "Transit Station", "matchScore": 0.2}, - "public_transport/stop_area": {"icon": "maki-bus", "fields": ["name", "ref", "network", "operator"], "geometry": ["relation"], "tags": {"type": "public_transport", "public_transport": "stop_area"}, "addTags": {"type": "public_transport", "public_transport": "stop_area", "public_transport:version": "2"}, "removeTags": {"type": "public_transport", "public_transport": "stop_area", "public_transport:version": "2"}, "reference": {"key": "public_transport", "value": "stop_area"}, "name": "Transit Stop Area"}, + "public_transport/stop_area": {"icon": "iD-relation", "fields": ["name", "ref", "network", "operator"], "geometry": ["relation"], "tags": {"type": "public_transport", "public_transport": "stop_area"}, "addTags": {"type": "public_transport", "public_transport": "stop_area", "public_transport:version": "2"}, "removeTags": {"type": "public_transport", "public_transport": "stop_area", "public_transport:version": "2"}, "reference": {"key": "public_transport", "value": "stop_area"}, "name": "Transit Stop Area"}, "public_transport/stop_position_aerialway": {"icon": "maki-aerialway", "fields": ["name", "ref_stop_position", "network", "operator"], "geometry": ["vertex"], "tags": {"public_transport": "stop_position", "aerialway": "yes"}, "reference": {"key": "public_transport", "value": "stop_position"}, "terms": ["aerialway", "cable car", "public transit", "public transportation", "transit", "transportation"], "name": "Aerialway Stopping Location"}, "public_transport/stop_position_bus": {"icon": "maki-bus", "fields": ["name", "ref_stop_position", "network", "operator"], "geometry": ["vertex"], "tags": {"public_transport": "stop_position", "bus": "yes"}, "reference": {"key": "public_transport", "value": "stop_position"}, "terms": ["bus", "public transit", "public transportation", "transit", "transportation"], "name": "Bus Stopping Location"}, "public_transport/stop_position_ferry": {"icon": "maki-ferry", "fields": ["name", "ref_stop_position", "network", "operator"], "geometry": ["vertex"], "tags": {"public_transport": "stop_position", "ferry": "yes"}, "reference": {"key": "public_transport", "value": "stop_position"}, "terms": ["boat", "dock", "ferry", "pier", "public transit", "public transportation", "transit", "transportation"], "name": "Ferry Stopping Location"}, @@ -1009,6 +1009,7 @@ "type/multipolygon": {"icon": "iD-multipolygon", "geometry": ["area", "relation"], "tags": {"type": "multipolygon"}, "removeTags": {}, "name": "Multipolygon", "searchable": false, "matchScore": 0.1}, "type/boundary": {"icon": "iD-boundary", "fields": ["name", "boundary"], "geometry": ["relation"], "tags": {"type": "boundary"}, "name": "Boundary"}, "type/boundary/administrative": {"icon": "iD-boundary", "fields": ["name", "admin_level"], "geometry": ["relation"], "tags": {"type": "boundary", "boundary": "administrative"}, "reference": {"key": "boundary", "value": "administrative"}, "name": "Administrative Boundary"}, + "type/enforcement": {"icon": "iD-relation", "fields": ["name", "enforcement"], "geometry": ["relation"], "tags": {"type": "enforcement"}, "name": "Enforcement"}, "type/restriction": {"icon": "iD-restriction", "fields": ["name", "restriction", "except"], "geometry": ["relation"], "tags": {"type": "restriction"}, "name": "Restriction"}, "type/restriction/no_left_turn": {"icon": "iD-restriction-no-left-turn", "fields": ["except"], "geometry": ["relation"], "tags": {"type": "restriction", "restriction": "no_left_turn"}, "name": "No Left Turn"}, "type/restriction/no_right_turn": {"icon": "iD-restriction-no-right-turn", "fields": ["except"], "geometry": ["relation"], "tags": {"type": "restriction", "restriction": "no_right_turn"}, "name": "No Right Turn"}, diff --git a/data/presets/presets/public_transport/stop_area.json b/data/presets/presets/public_transport/stop_area.json index 2d2b873fd..00396e5e9 100644 --- a/data/presets/presets/public_transport/stop_area.json +++ b/data/presets/presets/public_transport/stop_area.json @@ -1,5 +1,5 @@ { - "icon": "maki-bus", + "icon": "iD-relation", "fields": [ "name", "ref", diff --git a/data/presets/presets/type/enforcement.json b/data/presets/presets/type/enforcement.json new file mode 100644 index 000000000..cf13cb6b1 --- /dev/null +++ b/data/presets/presets/type/enforcement.json @@ -0,0 +1,14 @@ +{ + "icon": "iD-relation", + "fields": [ + "name", + "enforcement" + ], + "geometry": [ + "relation" + ], + "tags": { + "type": "enforcement" + }, + "name": "Enforcement" +} diff --git a/data/presets/schema/field.json b/data/presets/schema/field.json index b77a28413..41f8f7de1 100644 --- a/data/presets/schema/field.json +++ b/data/presets/schema/field.json @@ -129,6 +129,28 @@ "maxValue": { "description": "Maximum field value (number fields only)", "type": "integer" + }, + "prerequisiteTag": { + "description": "Tagging constraint for showing this field in the editor", + "type": { + "type": "object", + "properties": { + "key": { + "description": "The key of the required tag", + "type": "string", + "required": true + }, + "value": { + "description": "The value that the tag must have. (alternative to 'valueNot')", + "type": "string" + }, + "valueNot": { + "description": "The value that the tag cannot have. (alternative to 'value')", + "type": "string" + } + }, + "additionalProperties": false + } } }, "additionalProperties": false diff --git a/data/taginfo.json b/data/taginfo.json index 5e9d3173c..c76bf847f 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -5146,7 +5146,7 @@ "value": "stop_area", "description": "🄿 Transit Stop Area", "object_types": ["relation"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/bus-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/relation.svg?sanitize=true" }, { "key": "public_transport", @@ -6580,6 +6580,13 @@ "object_types": ["relation"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/boundary.svg?sanitize=true" }, + { + "key": "type", + "value": "enforcement", + "description": "🄿 Enforcement", + "object_types": ["relation"], + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/relation.svg?sanitize=true" + }, { "key": "type", "value": "restriction", @@ -7223,6 +7230,7 @@ {"key": "email", "description": "🄵 Email"}, {"key": "embankment", "description": "🄵 Type"}, {"key": "emergency", "description": "🄵 Emergency"}, + {"key": "enforcement", "description": "🄵 Type"}, {"key": "except", "description": "🄵 Exceptions"}, {"key": "faces", "description": "🄵 Faces"}, {"key": "fax", "description": "🄵 Fax"}, diff --git a/dist/locales/en.json b/dist/locales/en.json index 0a04218ef..c0da332ce 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2006,6 +2006,9 @@ "emergency": { "label": "Emergency" }, + "enforcement": { + "label": "Type" + }, "entrance": { "label": "Type" }, @@ -7270,6 +7273,10 @@ "name": "Administrative Boundary", "terms": "" }, + "type/enforcement": { + "name": "Enforcement", + "terms": "" + }, "type/restriction": { "name": "Restriction", "terms": "" diff --git a/modules/ui/field.js b/modules/ui/field.js index 27649d105..0d48513ae 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -48,10 +48,12 @@ export function uiField(context, presetField, entity, options) { dispatch.call('change', field, t, onInput); }); - // if this field cares about the entity, pass it along - if (entity && field.impl.entity) { + if (entity) { field.entityID = entity.id; - field.impl.entity(entity); + // if this field cares about the entity, pass it along + if (field.impl.entity) { + field.impl.entity(entity); + } } } @@ -229,11 +231,39 @@ export function uiField(context, presetField, entity, options) { } }; - + // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown field.isShown = function() { return _show || isPresent(); }; + // An allowed field can appear in the UI or in the 'Add field' dropdown. + // A non-allowed field is hidden from the user altogether + field.isAllowed = function() { + + if (isPresent()) { + // always allow a field with a value to display + return true; + } + + var prerequisiteTag = field.prerequisiteTag; + if (prerequisiteTag && field.entityID) { + if (prerequisiteTag.key) { + var value = context.graph().entity(field.entityID).tags[prerequisiteTag.key]; + if (value) { + if (prerequisiteTag.valueNot) { + return prerequisiteTag.valueNot !== value; + } + if (prerequisiteTag.value) { + return prerequisiteTag.value === value; + } + return true; + } else { + return false; + } + } + } + return true; + }; field.focus = function() { if (field.impl) { diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js index 51984aac8..08a7e2afb 100644 --- a/modules/ui/form_fields.js +++ b/modules/ui/form_fields.js @@ -16,9 +16,18 @@ export function uiFormFields(context) { } + formFields.tagsChanged = function() {}; + function render(selection, klass) { - var shown = _fieldsArr.filter(function(field) { return field.isShown(); }); - var notShown = _fieldsArr.filter(function(field) { return !field.isShown(); }); + + formFields.tagsChanged = function() { + render(selection, klass); + }; + + var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); }); + + var shown = allowedFields.filter(function(field) { return field.isShown(); }); + var notShown = allowedFields.filter(function(field) { return !field.isShown(); }); var container = selection.selectAll('.form-fields-container') .data([0]); diff --git a/modules/ui/preset_editor.js b/modules/ui/preset_editor.js index e688962db..0c3d46ce6 100644 --- a/modules/ui/preset_editor.js +++ b/modules/ui/preset_editor.js @@ -120,6 +120,7 @@ export function uiPresetEditor(context) { presetEditor.tags = function(val) { if (!arguments.length) return _tags; _tags = val; + formFields.tagsChanged(); // Don't reset _fieldsArr here. return presetEditor; }; diff --git a/test/spec/actions/split.js b/test/spec/actions/split.js index 833b3542f..9b6bb7e48 100644 --- a/test/spec/actions/split.js +++ b/test/spec/actions/split.js @@ -596,6 +596,266 @@ describe('iD.actionSplit', function () { }); }); + describe('splitting hat routes', function () { + var a = iD.osmNode({id: 'a', loc: [0, 0]}); + var b = iD.osmNode({id: 'b', loc: [1, 0]}); + var c = iD.osmNode({id: 'c', loc: [2, 1]}); + var d = iD.osmNode({id: 'd', loc: [3, 0]}); + var e = iD.osmNode({id: 'e', loc: [4, 0]}); + + // + // Situation: + // ###> c >### + // # # + // a --> b ~~~~~~> d ==> e + // + // Relation: ['-', '#', '~', '#', '='] + // + var hat1a = iD.coreGraph([ + a, b, c, d, e, + iD.osmWay({id: '-', nodes: ['a', 'b']}), + iD.osmWay({id: '#', nodes: ['b', 'c', 'd']}), + iD.osmWay({id: '~', nodes: ['b', 'd']}), + iD.osmWay({id: '=', nodes: ['d', 'e']}), + iD.osmRelation({ + id: 'r', members: [ + {id: '-', type: 'way'}, + {id: '#', type: 'way'}, + {id: '~', type: 'way'}, + {id: '#', type: 'way'}, + {id: '=', type: 'way'} + ] + }) + ]); + + // + // Situation: + // ###> c >### + // # # + // a --> b ~~~~~~> d ==> e + // + // Relation: ['-', '~', '#', '~', '='] + // + var hat1b = iD.coreGraph([ + a, b, c, d, e, + iD.osmWay({id: '-', nodes: ['a', 'b']}), + iD.osmWay({id: '#', nodes: ['b', 'c', 'd']}), + iD.osmWay({id: '~', nodes: ['b', 'd']}), + iD.osmWay({id: '=', nodes: ['d', 'e']}), + iD.osmRelation({ + id: 'r', members: [ + {id: '-', type: 'way'}, + {id: '~', type: 'way'}, + {id: '#', type: 'way'}, + {id: '~', type: 'way'}, + {id: '=', type: 'way'} + ] + }) + ]); + + // + // Situation: + // ###< c <### + // # # + // a --> b ~~~~~~> d ==> e + // + // Relation: ['-', '#', '~', '#', '='] + // + var hat2 = iD.coreGraph([ + a, b, c, d, e, + iD.osmWay({id: '-', nodes: ['a', 'b']}), + iD.osmWay({id: '#', nodes: ['d', 'c', 'b']}), + iD.osmWay({id: '~', nodes: ['b', 'd']}), + iD.osmWay({id: '=', nodes: ['d', 'e']}), + iD.osmRelation({ + id: 'r', members: [ + {id: '-', type: 'way'}, + {id: '#', type: 'way'}, + {id: '~', type: 'way'}, + {id: '#', type: 'way'}, + {id: '=', type: 'way'} + ] + }) + ]); + + // + // Situation: + // ###< c <### + // # # + // a --> b <~~~~~~ d ==> e + // + // Relation: ['-', '#', '~', '#', '='] + // + var hat3 = iD.coreGraph([ + a, b, c, d, e, + iD.osmWay({id: '-', nodes: ['a', 'b']}), + iD.osmWay({id: '#', nodes: ['d', 'c', 'b']}), + iD.osmWay({id: '~', nodes: ['d', 'b']}), + iD.osmWay({id: '=', nodes: ['d', 'e']}), + iD.osmRelation({ + id: 'r', members: [ + {id: '-', type: 'way'}, + {id: '#', type: 'way'}, + {id: '~', type: 'way'}, + {id: '#', type: 'way'}, + {id: '=', type: 'way'} + ] + }) + ]); + + // + // Situation: + // ###> c >### + // # # + // a --> b <~~~~~~ d ==> e + // + // Relation: ['-', '#', '~', '#', '='] + // + var hat4 = iD.coreGraph([ + a, b, c, d, e, + iD.osmWay({id: '-', nodes: ['a', 'b']}), + iD.osmWay({id: '#', nodes: ['b', 'c', 'd']}), + iD.osmWay({id: '~', nodes: ['d', 'b']}), + iD.osmWay({id: '=', nodes: ['d', 'e']}), + iD.osmRelation({ + id: 'r', members: [ + {id: '-', type: 'way'}, + {id: '#', type: 'way'}, + {id: '~', type: 'way'}, + {id: '#', type: 'way'}, + {id: '=', type: 'way'} + ] + }) + ]); + + // + // Situation: + // ###> c >### + // # # + // a <-- b ~~~~~~> d <== e + // + // Relation: ['-', '#', '~', '#', '='] + // + var hat5 = iD.coreGraph([ + a, b, c, d, e, + iD.osmWay({id: '-', nodes: ['b', 'a']}), + iD.osmWay({id: '#', nodes: ['b', 'c', 'd']}), + iD.osmWay({id: '~', nodes: ['b', 'd']}), + iD.osmWay({id: '=', nodes: ['e', 'd']}), + iD.osmRelation({ + id: 'r', members: [ + {id: '-', type: 'way'}, + {id: '#', type: 'way'}, + {id: '~', type: 'way'}, + {id: '#', type: 'way'}, + {id: '=', type: 'way'} + ] + }) + ]); + + it('splits hat1a route at c', function () { + // + // Expected result: + // ###> c >*** + // # * + // a --> b ~~~~~~> d ==> e + // + // Relation: ['-', '#', '*', '~', '#', '*', '='] + // + var graph = hat1a; + graph = iD.actionSplit('c', ['*'])(graph); + + expect(graph.entity('#').nodes).to.eql(['b', 'c']); + expect(graph.entity('*').nodes).to.eql(['c', 'd']); + expect(members(graph)).to.eql(['-', '#', '*', '~', '#', '*', '=']); + }); + + it('splits hat1b route at c', function () { + // + // Expected result: + // ###> c >*** + // # * + // a --> b ~~~~~~> d ==> e + // + // Relation: ['-', '~', '*', '#', '~', '='] + // + var graph = hat1b; + graph = iD.actionSplit('c', ['*'])(graph); + + expect(graph.entity('#').nodes).to.eql(['b', 'c']); + expect(graph.entity('*').nodes).to.eql(['c', 'd']); + expect(members(graph)).to.eql(['-', '~', '*', '#', '~', '=']); + }); + + it('splits hat2 route at c', function () { + // + // Expected result: + // ***< c <### + // * # + // a --> b ~~~~~~> d ==> e + // + // Relation: ['-', '*', '#', '~', '*', '#', '='] + // + var graph = hat2; + graph = iD.actionSplit('c', ['*'])(graph); + + expect(graph.entity('#').nodes).to.eql(['d', 'c']); + expect(graph.entity('*').nodes).to.eql(['c', 'b']); + expect(members(graph)).to.eql(['-', '*', '#', '~', '*', '#', '=']); + }); + + it('splits hat3 route at c', function () { + // + // Expected result: + // ***< c <### + // * # + // a --> b <~~~~~~ d ==> e + // + // Relation: ['-', '*', '#', '~', '*', '#', '='] + // + var graph = hat3; + graph = iD.actionSplit('c', ['*'])(graph); + + expect(graph.entity('#').nodes).to.eql(['d', 'c']); + expect(graph.entity('*').nodes).to.eql(['c', 'b']); + expect(members(graph)).to.eql(['-', '*', '#', '~', '*', '#', '=']); + }); + + it('splits hat4 route at c', function () { + // + // Expected result: + // ###> c >*** + // # * + // a --> b <~~~~~~ d ==> e + // + // Relation: ['-', '*', '#', '~', '*', '#', '='] + // + var graph = hat4; + graph = iD.actionSplit('c', ['*'])(graph); + + expect(graph.entity('#').nodes).to.eql(['b', 'c']); + expect(graph.entity('*').nodes).to.eql(['c', 'd']); + expect(members(graph)).to.eql(['-', '#', '*', '~', '#', '*', '=']); + }); + + it('splits hat5 route at c', function () { + // + // Expected result: + // ###> c >*** + // # * + // a <-- b ~~~~~~> d <== e + // + // Relation: ['-', '#', '*', '~', '#', '*', '='] + // + var graph = hat5; + graph = iD.actionSplit('c', ['*'])(graph); + + expect(graph.entity('#').nodes).to.eql(['b', 'c']); + expect(graph.entity('*').nodes).to.eql(['c', 'd']); + expect(members(graph)).to.eql(['-', '#', '*', '~', '#', '*', '=']); + }); + + }); describe('splitting spoon routes', function () { var a = iD.osmNode({ id: 'a', loc: [0, 0] });