mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-18 22:48:10 +02:00
Refactor common helper code to svg/helpers.js, add area nopes
This commit is contained in:
+34
-9
@@ -4,7 +4,7 @@ import _values from 'lodash-es/values';
|
||||
import { bisector as d3_bisector } from 'd3-array';
|
||||
|
||||
import { osmEntity, osmIsSimpleMultipolygonOuterMember } from '../osm';
|
||||
import { svgPath, svgTagClasses } from './index';
|
||||
import { svgPath, svgSegmentWay, svgTagClasses } from './index';
|
||||
|
||||
|
||||
export function svgAreas(projection, context) {
|
||||
@@ -42,16 +42,25 @@ export function svgAreas(projection, context) {
|
||||
|
||||
|
||||
function drawTargets(selection, graph, entities, filter) {
|
||||
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
|
||||
var getPath = svgPath(projection, graph);
|
||||
var passive = entities.filter(function(d) {
|
||||
return true;
|
||||
// return context.activeIDs().indexOf(d.id) === -1;
|
||||
var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
|
||||
var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
|
||||
var getPath = svgPath(projection).geojson;
|
||||
var activeID = context.activeID();
|
||||
|
||||
// The targets and nopes will be MultiLineString sub-segments of the ways
|
||||
var data = { targets: [], nopes: [] };
|
||||
|
||||
entities.forEach(function(way) {
|
||||
var features = svgSegmentWay(way, graph, activeID);
|
||||
data.targets.push.apply(data.targets, features.passive);
|
||||
data.nopes.push.apply(data.nopes, features.active);
|
||||
});
|
||||
|
||||
var targets = selection.selectAll('.area.target')
|
||||
|
||||
// Targets allow hover and vertex snapping
|
||||
var targets = selection.selectAll('.area.target-allowed')
|
||||
.filter(filter)
|
||||
.data(passive, function key(d) { return d.id; });
|
||||
.data(data.targets, function key(d) { return d.id; });
|
||||
|
||||
// exit
|
||||
targets.exit()
|
||||
@@ -62,7 +71,23 @@ export function svgAreas(projection, context) {
|
||||
.append('path')
|
||||
.merge(targets)
|
||||
.attr('d', getPath)
|
||||
.attr('class', function(d) { return 'way area target ' + fillClass + d.id; });
|
||||
.attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; });
|
||||
|
||||
|
||||
// NOPE
|
||||
var nopes = selection.selectAll('.area.target-nope')
|
||||
.data(data.nopes, function key(d) { return d.id; });
|
||||
|
||||
// exit
|
||||
nopes.exit()
|
||||
.remove();
|
||||
|
||||
// enter/update
|
||||
nopes.enter()
|
||||
.append('path')
|
||||
.merge(nopes)
|
||||
.attr('d', getPath)
|
||||
.attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; });
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { geoPath as d3_geoPath } from 'd3-geo';
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { geoPolygonIntersectsPolygon } from '../geo';
|
||||
import {
|
||||
data,
|
||||
dataImperial,
|
||||
dataDriveLeft
|
||||
} from '../../data';
|
||||
import { data, dataImperial, dataDriveLeft } from '../../data';
|
||||
import { svgPath } from './index';
|
||||
|
||||
|
||||
export function svgDebug(projection, context) {
|
||||
@@ -27,8 +23,6 @@ export function svgDebug(projection, context) {
|
||||
var showsImperial = context.getDebug('imperial');
|
||||
var showsDriveLeft = context.getDebug('driveLeft');
|
||||
var showsTouchTargets = context.getDebug('target');
|
||||
var path = d3_geoPath(projection);
|
||||
|
||||
|
||||
var debugData = [];
|
||||
if (showsTile) {
|
||||
@@ -134,7 +128,7 @@ export function svgDebug(projection, context) {
|
||||
|
||||
// update
|
||||
layer.selectAll('path')
|
||||
.attr('d', path);
|
||||
.attr('d', svgPath(projection).geojson);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
import _extend from 'lodash-es/extend';
|
||||
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoPath as d3_geoPath,
|
||||
geoStream as d3_geoStream
|
||||
} from 'd3-geo';
|
||||
|
||||
import { geoEuclideanDistance } from '../geo';
|
||||
|
||||
|
||||
// Touch targets control which other vertices we can drag a vertex onto.
|
||||
//
|
||||
// - the activeID - nope
|
||||
// - 1 away (adjacent) to the activeID - yes (vertices will be merged)
|
||||
// - 2 away from the activeID - nope (would create a self intersecting segment)
|
||||
// - all others on a linear way - yes
|
||||
// - all others on a closed way - nope (would create a self intersecting polygon)
|
||||
//
|
||||
// returns
|
||||
// 0 = active vertex - no touch/connect
|
||||
// 1 = passive vertex - yes touch/connect
|
||||
// 2 = adjacent vertex - yes but pay attention segmenting a line here
|
||||
//
|
||||
export function svgPassiveVertex(node, graph, activeID) {
|
||||
if (!activeID) return 1;
|
||||
if (activeID === node.id) return 0;
|
||||
|
||||
var parents = graph.parentWays(node);
|
||||
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
var nodes = parents[i].nodes;
|
||||
var isClosed = parents[i].isClosed();
|
||||
for (var j = 0; j < nodes.length; j++) { // find this vertex, look nearby
|
||||
if (nodes[j] === node.id) {
|
||||
var ix1 = j - 2;
|
||||
var ix2 = j - 1;
|
||||
var ix3 = j + 1;
|
||||
var ix4 = j + 2;
|
||||
|
||||
if (isClosed) { // wraparound if needed
|
||||
var max = nodes.length - 1;
|
||||
if (ix1 < 0) ix1 = max + ix1;
|
||||
if (ix2 < 0) ix2 = max + ix2;
|
||||
if (ix3 > max) ix3 = ix3 - max;
|
||||
if (ix4 > max) ix4 = ix4 - max;
|
||||
}
|
||||
|
||||
if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
|
||||
else if (nodes[ix2] === activeID) return 2; // ok - adjacent
|
||||
else if (nodes[ix3] === activeID) return 2; // ok - adjacent
|
||||
else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
|
||||
else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1; // ok
|
||||
}
|
||||
|
||||
|
||||
export function svgOneWaySegments(projection, graph, dt) {
|
||||
return function(entity) {
|
||||
var i = 0;
|
||||
var offset = dt;
|
||||
var segments = [];
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
|
||||
var a, b;
|
||||
|
||||
if (entity.tags.oneway === '-1') {
|
||||
coordinates.reverse();
|
||||
}
|
||||
|
||||
d3_geoStream({
|
||||
type: 'LineString',
|
||||
coordinates: coordinates
|
||||
}, projection.stream(clip({
|
||||
lineStart: function() {},
|
||||
lineEnd: function() { a = null; },
|
||||
point: function(x, y) {
|
||||
b = [x, y];
|
||||
|
||||
if (a) {
|
||||
var span = geoEuclideanDistance(a, b) - offset;
|
||||
|
||||
if (span >= 0) {
|
||||
var angle = Math.atan2(b[1] - a[1], b[0] - a[0]);
|
||||
var dx = dt * Math.cos(angle);
|
||||
var dy = dt * Math.sin(angle);
|
||||
var p = [
|
||||
a[0] + offset * Math.cos(angle),
|
||||
a[1] + offset * Math.sin(angle)
|
||||
];
|
||||
var segment = 'M' + a[0] + ',' + a[1] + 'L' + p[0] + ',' + p[1];
|
||||
|
||||
for (span -= dt; span >= 0; span -= dt) {
|
||||
p[0] += dx;
|
||||
p[1] += dy;
|
||||
segment += 'L' + p[0] + ',' + p[1];
|
||||
}
|
||||
|
||||
segment += 'L' + b[0] + ',' + b[1];
|
||||
segments.push({id: entity.id, index: i, d: segment});
|
||||
}
|
||||
|
||||
offset = -span;
|
||||
i++;
|
||||
}
|
||||
|
||||
a = b;
|
||||
}
|
||||
})));
|
||||
|
||||
return segments;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function svgPath(projection, graph, isArea) {
|
||||
|
||||
// Explanation of magic numbers:
|
||||
// "padding" here allows space for strokes to extend beyond the viewport,
|
||||
// so that the stroke isn't drawn along the edge of the viewport when
|
||||
// the shape is clipped.
|
||||
//
|
||||
// When drawing lines, pad viewport by 5px.
|
||||
// When drawing areas, pad viewport by 65px in each direction to allow
|
||||
// for 60px area fill stroke (see ".fill-partial path.fill" css rule)
|
||||
|
||||
var cache = {};
|
||||
var padding = isArea ? 65 : 5;
|
||||
var viewport = projection.clipExtent();
|
||||
var paddedExtent = [
|
||||
[viewport[0][0] - padding, viewport[0][1] - padding],
|
||||
[viewport[1][0] + padding, viewport[1][1] + padding]
|
||||
];
|
||||
var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
|
||||
var project = projection.stream;
|
||||
var path = d3_geoPath()
|
||||
.projection({stream: function(output) { return project(clip(output)); }});
|
||||
|
||||
var svgpath = function(entity) {
|
||||
if (entity.id in cache) {
|
||||
return cache[entity.id];
|
||||
} else {
|
||||
return cache[entity.id] = path(entity.asGeoJSON(graph));
|
||||
}
|
||||
};
|
||||
|
||||
svgpath.geojson = path;
|
||||
|
||||
return svgpath;
|
||||
}
|
||||
|
||||
|
||||
export function svgPointTransform(projection) {
|
||||
return function(entity) {
|
||||
// http://jsperf.com/short-array-join
|
||||
var pt = projection(entity.loc);
|
||||
return 'translate(' + pt[0] + ',' + pt[1] + ')';
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function svgRelationMemberTags(graph) {
|
||||
return function(entity) {
|
||||
var tags = entity.tags;
|
||||
graph.parentRelations(entity).forEach(function(relation) {
|
||||
var type = relation.tags.type;
|
||||
if (type === 'multipolygon' || type === 'boundary') {
|
||||
tags = _extend({}, relation.tags, tags);
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function svgSegmentWay(way, graph, activeID) {
|
||||
var features = { passive: [], active: [] };
|
||||
var coordGroups = { passive: [], active: [] };
|
||||
var segment = [];
|
||||
var startType = null; // 0 = active, 1 = passive, 2 = adjacent
|
||||
var currType = null;
|
||||
var node;
|
||||
|
||||
for (var i = 0; i < way.nodes.length; i++) {
|
||||
if (way.nodes[i] === activeID) { // vertex is the activeID
|
||||
segment = []; // draw no segment here
|
||||
startType = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
node = graph.entity(way.nodes[i]);
|
||||
currType = svgPassiveVertex(node, graph, activeID);
|
||||
|
||||
if (startType === null) {
|
||||
startType = currType;
|
||||
}
|
||||
|
||||
if (currType !== startType) { // line changes here - try to save a segment
|
||||
|
||||
if (segment.length > 0) { // finish previous segment
|
||||
segment.push(node.loc);
|
||||
if (startType === 2 || currType === 2) { // one adjacent vertex
|
||||
coordGroups.active.push(segment);
|
||||
} else if (startType === 0 && currType === 0) { // both active vertices
|
||||
coordGroups.active.push(segment);
|
||||
} else {
|
||||
coordGroups.passive.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
segment = [];
|
||||
startType = currType;
|
||||
}
|
||||
|
||||
segment.push(node.loc);
|
||||
}
|
||||
|
||||
// complete whatever segment we ended on
|
||||
if (segment.length > 1) {
|
||||
if (startType === 2 || currType === 2) { // one adjacent vertex
|
||||
coordGroups.active.push(segment);
|
||||
} else if (startType === 0 && currType === 0) { // both active vertices
|
||||
coordGroups.active.push(segment);
|
||||
} else {
|
||||
coordGroups.passive.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (coordGroups.passive.length) {
|
||||
features.passive.push({
|
||||
'type': 'Feature',
|
||||
'id': way.id,
|
||||
'geometry': {
|
||||
'type': 'MultiLineString',
|
||||
'coordinates': coordGroups.passive
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (coordGroups.active.length) {
|
||||
features.active.push({
|
||||
'type': 'Feature',
|
||||
'id': way.id + '-nope', // break the ids on purpose
|
||||
'geometry': {
|
||||
'type': 'MultiLineString',
|
||||
'coordinates': coordGroups.active
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
@@ -9,13 +9,15 @@ export { svgLines } from './lines.js';
|
||||
export { svgMapillaryImages } from './mapillary_images.js';
|
||||
export { svgMapillarySigns } from './mapillary_signs.js';
|
||||
export { svgMidpoints } from './midpoints.js';
|
||||
export { svgOneWaySegments } from './one_way_segments.js';
|
||||
export { svgOneWaySegments } from './helpers.js';
|
||||
export { svgOpenstreetcamImages } from './openstreetcam_images.js';
|
||||
export { svgOsm } from './osm.js';
|
||||
export { svgPath } from './path.js';
|
||||
export { svgPointTransform } from './point_transform.js';
|
||||
export { svgPassiveVertex } from './helpers.js';
|
||||
export { svgPath } from './helpers.js';
|
||||
export { svgPointTransform } from './helpers.js';
|
||||
export { svgPoints } from './points.js';
|
||||
export { svgRelationMemberTags } from './relation_member_tags.js';
|
||||
export { svgRelationMemberTags } from './helpers.js';
|
||||
export { svgSegmentWay } from './helpers.js';
|
||||
export { svgTagClasses } from './tag_classes.js';
|
||||
export { svgTurns } from './turns.js';
|
||||
export { svgVertices } from './vertices.js';
|
||||
|
||||
+7
-128
@@ -4,13 +4,13 @@ import _flatten from 'lodash-es/flatten';
|
||||
import _forOwn from 'lodash-es/forOwn';
|
||||
import _map from 'lodash-es/map';
|
||||
|
||||
import { geoPath as d3_geoPath } from 'd3-geo';
|
||||
import { range as d3_range } from 'd3-array';
|
||||
|
||||
import {
|
||||
svgOneWaySegments,
|
||||
svgPath,
|
||||
svgRelationMemberTags,
|
||||
svgSegmentWay,
|
||||
svgTagClasses
|
||||
} from './index';
|
||||
|
||||
@@ -40,140 +40,20 @@ export function svgLines(projection, context) {
|
||||
function drawTargets(selection, graph, entities, filter) {
|
||||
var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
|
||||
var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
|
||||
var getPath = svgPath(projection, graph).geojson;
|
||||
// var getPath = d3_geoPath(projection);
|
||||
|
||||
var getPath = svgPath(projection).geojson;
|
||||
var activeID = context.activeID();
|
||||
|
||||
// Rather than drawing lines directly, we'll cut out pieces
|
||||
// depending on which parts are active.
|
||||
// The targets and nopes will be MultiLineString sub-segments of the ways
|
||||
var data = { targets: [], nopes: [] };
|
||||
|
||||
// Touch targets control which other vertices we can drag a vertex onto.
|
||||
// - the activeID - nope
|
||||
// - next to the activeID - yes (vertices will be merged)
|
||||
// - 2 away from the activeID - nope (would create a self intersecting segment)
|
||||
// - all others on a closed way - nope (would create a self intersecting polygon)
|
||||
//
|
||||
// 0 = active vertex - no touch/connect
|
||||
// 1 = passive vertex - yes touch/connect
|
||||
// 2 = adjacent vertex - special rules
|
||||
function passive(d) {
|
||||
if (!activeID) return 1;
|
||||
if (activeID === d.id) return 0;
|
||||
|
||||
var parents = graph.parentWays(d);
|
||||
var i, j;
|
||||
|
||||
for (i = 0; i < parents.length; i++) {
|
||||
var nodes = parents[i].nodes;
|
||||
var isClosed = parents[i].isClosed();
|
||||
for (j = 0; j < nodes.length; j++) { // find this vertex, look nearby
|
||||
if (nodes[j] === d.id) {
|
||||
var ix1 = j - 2;
|
||||
var ix2 = j - 1;
|
||||
var ix3 = j + 1;
|
||||
var ix4 = j + 2;
|
||||
|
||||
if (isClosed) { // wraparound if needed
|
||||
var max = nodes.length - 1;
|
||||
if (ix1 < 0) ix1 = max + ix1;
|
||||
if (ix2 < 0) ix2 = max + ix2;
|
||||
if (ix3 > max) ix3 = ix3 - max;
|
||||
if (ix4 > max) ix4 = ix4 - max;
|
||||
}
|
||||
|
||||
if (nodes[ix1] === activeID) return 0; // prevent self intersect
|
||||
else if (nodes[ix2] === activeID) return 2; // adjacent - ok!
|
||||
else if (nodes[ix3] === activeID) return 2; // adjacent - ok!
|
||||
else if (nodes[ix4] === activeID) return 0; // prevent self intersect
|
||||
else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // prevent self intersect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
entities.forEach(function(way) {
|
||||
var coordGroups = { passive: [], active: [] };
|
||||
var segment = [];
|
||||
var startType = null; // 0 = active, 1 = passive, 2 = adjacent
|
||||
var currType = null;
|
||||
var node;
|
||||
|
||||
for (var i = 0; i < way.nodes.length; i++) {
|
||||
|
||||
if (way.nodes[i] === activeID) { // vertex is the activeID
|
||||
segment = []; // draw no segment here
|
||||
startType = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
node = graph.entity(way.nodes[i]);
|
||||
currType = passive(node);
|
||||
|
||||
if (startType === null) {
|
||||
startType = currType;
|
||||
}
|
||||
|
||||
if (currType !== startType) { // line changes here - try to save a segment
|
||||
|
||||
if (segment.length > 0) { // finish previous segment
|
||||
segment.push(node.loc);
|
||||
|
||||
if (startType === 2 || currType === 2) { // one adjacent vertex
|
||||
coordGroups.active.push(segment);
|
||||
} else if (startType === 0 && currType === 0) { // both active vertices
|
||||
coordGroups.active.push(segment);
|
||||
} else {
|
||||
coordGroups.passive.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
segment = [];
|
||||
startType = currType;
|
||||
}
|
||||
|
||||
segment.push(node.loc);
|
||||
}
|
||||
|
||||
// complete whatever segment we ended on
|
||||
if (segment.length > 1) {
|
||||
if (startType === 2 || currType === 2) { // one adjacent vertex
|
||||
coordGroups.active.push(segment);
|
||||
} else if (startType === 0 && currType === 0) { // both active vertices
|
||||
coordGroups.active.push(segment);
|
||||
} else {
|
||||
coordGroups.passive.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (coordGroups.passive.length) {
|
||||
data.targets.push({
|
||||
'type': 'Feature',
|
||||
'id': way.id,
|
||||
'geometry': {
|
||||
'type': 'MultiLineString',
|
||||
'coordinates': coordGroups.passive
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (coordGroups.active.length) {
|
||||
data.nopes.push({
|
||||
'type': 'Feature',
|
||||
'id': way.id + '-nope', // break the ids on purpose
|
||||
'geometry': {
|
||||
'type': 'MultiLineString',
|
||||
'coordinates': coordGroups.active
|
||||
}
|
||||
});
|
||||
}
|
||||
var features = svgSegmentWay(way, graph, activeID);
|
||||
data.targets.push.apply(data.targets, features.passive);
|
||||
data.nopes.push.apply(data.nopes, features.active);
|
||||
});
|
||||
|
||||
|
||||
// Places to hover and connect
|
||||
// Targets allow hover and vertex snapping
|
||||
var targets = selection.selectAll('.line.target-allowed')
|
||||
.filter(filter)
|
||||
.data(data.targets, function key(d) { return d.id; });
|
||||
@@ -204,7 +84,6 @@ export function svgLines(projection, context) {
|
||||
.merge(nopes)
|
||||
.attr('d', getPath)
|
||||
.attr('class', function(d) { return 'way line target target-nope ' + nopeClass + d.id; });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoPath as d3_geoPath
|
||||
} from 'd3-geo';
|
||||
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { svgPointTransform } from './point_transform';
|
||||
import { svgPath, svgPointTransform } from './index';
|
||||
import { services } from '../services';
|
||||
|
||||
|
||||
export function svgMapillaryImages(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000),
|
||||
minZoom = 12,
|
||||
minMarkerZoom = 16,
|
||||
minViewfieldZoom = 18,
|
||||
layer = d3_select(null),
|
||||
_mapillary;
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
|
||||
var minZoom = 12;
|
||||
var minMarkerZoom = 16;
|
||||
var minViewfieldZoom = 18;
|
||||
var layer = d3_select(null);
|
||||
var _mapillary;
|
||||
|
||||
|
||||
function init() {
|
||||
@@ -128,25 +121,19 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
var sequences = (service ? service.sequences(projection) : []);
|
||||
var images = (service && showMarkers ? service.images(projection) : []);
|
||||
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var project = projection.stream;
|
||||
var makePath = d3_geoPath().projection({ stream: function(output) {
|
||||
return project(clip(output));
|
||||
}});
|
||||
|
||||
var traces = layer.selectAll('.sequences').selectAll('.sequence')
|
||||
.data(sequences, function(d) { return d.properties.key; });
|
||||
|
||||
// exit
|
||||
traces.exit()
|
||||
.remove();
|
||||
|
||||
// enter/update
|
||||
traces = traces.enter()
|
||||
.append('path')
|
||||
.attr('class', 'sequence')
|
||||
.merge(traces);
|
||||
|
||||
traces
|
||||
.attr('d', makePath);
|
||||
.merge(traces)
|
||||
.attr('d', svgPath(projection).geojson);
|
||||
|
||||
|
||||
var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
|
||||
|
||||
@@ -5,10 +5,10 @@ import { services } from '../services';
|
||||
|
||||
|
||||
export function svgMapillarySigns(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000),
|
||||
minZoom = 12,
|
||||
layer = d3_select(null),
|
||||
_mapillary;
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
|
||||
var minZoom = 12;
|
||||
var layer = d3_select(null);
|
||||
var _mapillary;
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoStream as d3_geoStream
|
||||
} from 'd3-geo';
|
||||
|
||||
import { geoEuclideanDistance } from '../geo';
|
||||
|
||||
|
||||
export function svgOneWaySegments(projection, graph, dt) {
|
||||
return function(entity) {
|
||||
var a,
|
||||
b,
|
||||
i = 0,
|
||||
offset = dt,
|
||||
segments = [],
|
||||
clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream,
|
||||
coordinates = graph.childNodes(entity).map(function(n) {
|
||||
return n.loc;
|
||||
});
|
||||
|
||||
if (entity.tags.oneway === '-1') coordinates.reverse();
|
||||
|
||||
d3_geoStream({
|
||||
type: 'LineString',
|
||||
coordinates: coordinates
|
||||
}, projection.stream(clip({
|
||||
lineStart: function() {},
|
||||
lineEnd: function() {
|
||||
a = null;
|
||||
},
|
||||
point: function(x, y) {
|
||||
b = [x, y];
|
||||
|
||||
if (a) {
|
||||
var span = geoEuclideanDistance(a, b) - offset;
|
||||
|
||||
if (span >= 0) {
|
||||
var angle = Math.atan2(b[1] - a[1], b[0] - a[0]),
|
||||
dx = dt * Math.cos(angle),
|
||||
dy = dt * Math.sin(angle),
|
||||
p = [a[0] + offset * Math.cos(angle),
|
||||
a[1] + offset * Math.sin(angle)];
|
||||
|
||||
var segment = 'M' + a[0] + ',' + a[1] +
|
||||
'L' + p[0] + ',' + p[1];
|
||||
|
||||
for (span -= dt; span >= 0; span -= dt) {
|
||||
p[0] += dx;
|
||||
p[1] += dy;
|
||||
segment += 'L' + p[0] + ',' + p[1];
|
||||
}
|
||||
|
||||
segment += 'L' + b[0] + ',' + b[1];
|
||||
segments.push({id: entity.id, index: i, d: segment});
|
||||
}
|
||||
|
||||
offset = -span;
|
||||
i++;
|
||||
}
|
||||
|
||||
a = b;
|
||||
}
|
||||
})));
|
||||
|
||||
return segments;
|
||||
};
|
||||
}
|
||||
@@ -1,23 +1,16 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoPath as d3_geoPath
|
||||
} from 'd3-geo';
|
||||
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { svgPointTransform } from './point_transform';
|
||||
import { svgPath, svgPointTransform } from './index';
|
||||
import { services } from '../services';
|
||||
|
||||
|
||||
export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000),
|
||||
minZoom = 12,
|
||||
minMarkerZoom = 16,
|
||||
minViewfieldZoom = 18,
|
||||
layer = d3_select(null),
|
||||
_openstreetcam;
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
|
||||
var minZoom = 12;
|
||||
var minMarkerZoom = 16;
|
||||
var minViewfieldZoom = 18;
|
||||
var layer = d3_select(null);
|
||||
var _openstreetcam;
|
||||
|
||||
|
||||
function init() {
|
||||
@@ -128,25 +121,19 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
var sequences = (service ? service.sequences(projection) : []);
|
||||
var images = (service && showMarkers ? service.images(projection) : []);
|
||||
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var project = projection.stream;
|
||||
var makePath = d3_geoPath().projection({ stream: function(output) {
|
||||
return project(clip(output));
|
||||
}});
|
||||
|
||||
var traces = layer.selectAll('.sequences').selectAll('.sequence')
|
||||
.data(sequences, function(d) { return d.properties.key; });
|
||||
|
||||
// exit
|
||||
traces.exit()
|
||||
.remove();
|
||||
|
||||
// enter/update
|
||||
traces = traces.enter()
|
||||
.append('path')
|
||||
.attr('class', 'sequence')
|
||||
.merge(traces);
|
||||
|
||||
traces
|
||||
.attr('d', makePath);
|
||||
.merge(traces)
|
||||
.attr('d', svgPath(projection).geojson);
|
||||
|
||||
|
||||
var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoPath as d3_geoPath
|
||||
} from 'd3-geo';
|
||||
|
||||
|
||||
export function svgPath(projection, graph, isArea) {
|
||||
|
||||
// Explanation of magic numbers:
|
||||
// "padding" here allows space for strokes to extend beyond the viewport,
|
||||
// so that the stroke isn't drawn along the edge of the viewport when
|
||||
// the shape is clipped.
|
||||
//
|
||||
// When drawing lines, pad viewport by 5px.
|
||||
// When drawing areas, pad viewport by 65px in each direction to allow
|
||||
// for 60px area fill stroke (see ".fill-partial path.fill" css rule)
|
||||
|
||||
var cache = {};
|
||||
var padding = isArea ? 65 : 5;
|
||||
var viewport = projection.clipExtent();
|
||||
var paddedExtent = [
|
||||
[viewport[0][0] - padding, viewport[0][1] - padding],
|
||||
[viewport[1][0] + padding, viewport[1][1] + padding]
|
||||
];
|
||||
var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
|
||||
var project = projection.stream;
|
||||
var path = d3_geoPath()
|
||||
.projection({stream: function(output) { return project(clip(output)); }});
|
||||
|
||||
var svgpath = function(entity) {
|
||||
if (entity.id in cache) {
|
||||
return cache[entity.id];
|
||||
} else {
|
||||
return cache[entity.id] = path(entity.asGeoJSON(graph));
|
||||
}
|
||||
};
|
||||
|
||||
svgpath.geojson = path;
|
||||
|
||||
return svgpath;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export function svgPointTransform(projection) {
|
||||
return function(entity) {
|
||||
// http://jsperf.com/short-array-join
|
||||
var pt = projection(entity.loc);
|
||||
return 'translate(' + pt[0] + ',' + pt[1] + ')';
|
||||
};
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import _extend from 'lodash-es/extend';
|
||||
|
||||
|
||||
export function svgRelationMemberTags(graph) {
|
||||
return function(entity) {
|
||||
var tags = entity.tags;
|
||||
graph.parentRelations(entity).forEach(function(relation) {
|
||||
var type = relation.tags.type;
|
||||
if (type === 'multipolygon' || type === 'boundary') {
|
||||
tags = _extend({}, relation.tags, tags);
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
};
|
||||
}
|
||||
+7
-52
@@ -6,7 +6,7 @@ import { select as d3_select } from 'd3-selection';
|
||||
import { dataFeatureIcons } from '../../data';
|
||||
import { geoScaleToZoom } from '../geo';
|
||||
import { osmEntity } from '../osm';
|
||||
import { svgPointTransform } from './index';
|
||||
import { svgPassiveVertex, svgPointTransform } from './index';
|
||||
|
||||
|
||||
export function svgVertices(projection, context) {
|
||||
@@ -187,67 +187,22 @@ export function svgVertices(projection, context) {
|
||||
var activeID = context.activeID();
|
||||
var data = { targets: [], nopes: [] };
|
||||
|
||||
// Touch targets control which other vertices we can drag a vertex onto.
|
||||
// - the activeID - nope
|
||||
// - next to the activeID - yes (vertices will be merged)
|
||||
// - 2 away from the activeID - nope (would create a self intersecting segment)
|
||||
// - all others on a closed way - nope (would create a self intersecting polygon)
|
||||
//
|
||||
// 0 = active vertex - no touch/connect
|
||||
// 1 = passive vertex - yes touch/connect
|
||||
// 2 = adjacent vertex - special rules
|
||||
function passive(d) {
|
||||
if (!activeID) return 1;
|
||||
if (activeID === d.id) return 0;
|
||||
|
||||
var parents = graph.parentWays(d);
|
||||
var i, j;
|
||||
|
||||
for (i = 0; i < parents.length; i++) {
|
||||
var nodes = parents[i].nodes;
|
||||
var isClosed = parents[i].isClosed();
|
||||
for (j = 0; j < nodes.length; j++) { // find this vertex, look nearby
|
||||
if (nodes[j] === d.id) {
|
||||
var ix1 = j - 2;
|
||||
var ix2 = j - 1;
|
||||
var ix3 = j + 1;
|
||||
var ix4 = j + 2;
|
||||
|
||||
if (isClosed) { // wraparound if needed
|
||||
var max = nodes.length - 1;
|
||||
if (ix1 < 0) ix1 = max + ix1;
|
||||
if (ix2 < 0) ix2 = max + ix2;
|
||||
if (ix3 > max) ix3 = ix3 - max;
|
||||
if (ix4 > max) ix4 = ix4 - max;
|
||||
}
|
||||
|
||||
if (nodes[ix1] === activeID) return 0; // prevent self intersect
|
||||
else if (nodes[ix2] === activeID) return 2; // adjacent - ok!
|
||||
else if (nodes[ix3] === activeID) return 2; // adjacent - ok!
|
||||
else if (nodes[ix4] === activeID) return 0; // prevent self intersect
|
||||
else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // prevent self intersect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
entities.forEach(function(node) {
|
||||
if (activeID === node.id) return; // draw no vertex on the activeID
|
||||
if (activeID === node.id) return; // draw no target on the activeID
|
||||
|
||||
var currType = passive(node);
|
||||
var currType = svgPassiveVertex(node, graph, activeID);
|
||||
if (currType !== 0) {
|
||||
data.targets.push(node); // passive or adjacent - allow to connect
|
||||
data.targets.push(node); // passive or adjacent - allow to connect
|
||||
} else {
|
||||
data.nopes.push({
|
||||
id: node.id + '-nope', // not a real osmNode, break the id on purpose
|
||||
id: node.id + '-nope', // not a real osmNode, break the id on purpose
|
||||
loc: node.loc
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Targets allow hover and vertex snapping
|
||||
var targets = selection.selectAll('.vertex.target-allowed')
|
||||
.filter(filter)
|
||||
.data(data.targets, function key(d) { return d.id; });
|
||||
|
||||
Reference in New Issue
Block a user