diff --git a/modules/renderer/photos.js b/modules/renderer/photos.js index 9a177afab..ff10aa22a 100644 --- a/modules/renderer/photos.js +++ b/modules/renderer/photos.js @@ -157,15 +157,7 @@ export function rendererPhotos(context) { } photos.shouldFilterByDate = function() { - return showsLayer('mapillary') || showsLayer('kartaview') || showsLayer('streetside') || showsLayer('vegbilder'); - }; - - photos.shouldFilterByMaxAge = function(){ - return false; - }; - - photos.shouldFilterDateBySlider = function(){ - return showsLayer('panoramax'); + return showsLayer('mapillary') || showsLayer('kartaview') || showsLayer('streetside') || showsLayer('vegbilder') || showsLayer('panoramax'); }; photos.shouldFilterByPhotoType = function() { diff --git a/modules/services/panoramax.js b/modules/services/panoramax.js index 6f7e22ebc..a08ac1db0 100644 --- a/modules/services/panoramax.js +++ b/modules/services/panoramax.js @@ -330,6 +330,10 @@ export default { } }, + getActiveImage: function(){ + return _activeImage; + }, + // Update the currently highlighted sequence and selected bubble. setStyles: function(context, hovered) { const hoveredImageId = hovered && hovered.id; diff --git a/modules/svg/layers.js b/modules/svg/layers.js index 4db0de629..59a014cc1 100644 --- a/modules/svg/layers.js +++ b/modules/svg/layers.js @@ -41,8 +41,8 @@ export function svgLayers(projection, context) { { id: 'mapillary-signs', layer: svgMapillarySigns(projection, context, dispatch) }, { id: 'kartaview', layer: svgKartaviewImages(projection, context, dispatch) }, { id: 'mapilio', layer: svgMapilioImages(projection, context, dispatch) }, - { id: 'panoramax', layer: svgPanoramaxImages(projection, context, dispatch) }, { id: 'vegbilder', layer: svgVegbilder(projection, context, dispatch) }, + { id: 'panoramax', layer: svgPanoramaxImages(projection, context, dispatch) }, { id: 'local-photos', layer: svgLocalPhotos(projection, context, dispatch) }, { id: 'debug', layer: svgDebug(projection, context, dispatch) }, { id: 'geolocate', layer: svgGeolocate(projection, context, dispatch) }, diff --git a/modules/svg/panoramax_images.js b/modules/svg/panoramax_images.js index 616ec75e1..c536ea2ac 100644 --- a/modules/svg/panoramax_images.js +++ b/modules/svg/panoramax_images.js @@ -10,7 +10,6 @@ export function svgPanoramaxImages(projection, context, dispatch) { const imageMinZoom = 15; const lineMinZoom = 10; const viewFieldZoomLevel = 18; - const maxOldestYear = 2010; let layer = d3_select(null); let _panoramax; let _viewerYaw = 0; @@ -137,7 +136,7 @@ export function svgPanoramaxImages(projection, context, dispatch) { function transform(d) { let t = svgPointTransform(projection)(d); var rot = d.heading + _viewerYaw; - if (rot) { + if (rot && !isNaN(rot)) { t += ' rotate(' + Math.floor(rot) + ',0,0)'; } return t; @@ -186,51 +185,6 @@ export function svgPanoramaxImages(projection, context, dispatch) { if (service) service.setStyles(context, null); } - function updateYearSlider(minYear){ - let maxYear = new Date(); - maxYear = maxYear.getFullYear(); - let slider = d3_select('.list-option-date-slider'); - - if (slider && minYear){ - let sliderWrap = slider.select(function() { return this.parentNode; }); - let sliderLabel = sliderWrap.select('.year-selected'); - - sliderWrap.selectAll('datalist').remove(); - - let datalist = sliderWrap.append('datalist') - .attr('id', 'dateValues') - .attr('class', 'year-datalist'); - - minYear = parseInt(minYear, 10); - - if (minYear < maxOldestYear) minYear = maxOldestYear; - - let currYear = sliderLabel.html(); - currYear = currYear.substring(0, 4); - currYear = parseInt(currYear, 10); - - let sliderValue = maxYear - (currYear - minYear); - - if (minYear > currYear){ - sliderValue = maxYear; - sliderLabel.html(minYear + ' - ' + maxYear); - } - - slider.attr('value', sliderValue); - slider.attr('min', minYear); - - datalist - .append('option') - .attr('value', minYear) - .attr('label', minYear); - - datalist - .append('option') - .attr('value', maxYear) - .attr('label', maxYear); - } - } - async function update() { const zoom = ~~context.map().zoom(); const showViewfields = (zoom >= viewFieldZoomLevel); @@ -242,8 +196,6 @@ export function svgPanoramaxImages(projection, context, dispatch) { images = await filterImages(images); sequences = await filterSequences(sequences, service); - let oldestDate = (service ? service.getOldestDate() : null); - let traces = layer.selectAll('.sequences').selectAll('.sequence') .data(sequences, function(d) { return d.id; }); @@ -307,8 +259,6 @@ export function svgPanoramaxImages(projection, context, dispatch) { .attr('transform', 'scale(1.5,1.5),translate(-8, -13)') .attr('d', viewfieldPath); - if (oldestDate) updateYearSlider(oldestDate.substring(0, 4)); - function viewfieldPath() { if (this.parentNode.__data__.isPano) { return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; diff --git a/modules/ui/sections/photo_overlays.js b/modules/ui/sections/photo_overlays.js index a572b44eb..7d5c35ea2 100644 --- a/modules/ui/sections/photo_overlays.js +++ b/modules/ui/sections/photo_overlays.js @@ -30,7 +30,6 @@ export function uiSectionPhotoOverlays(context) { .merge(container) .call(drawPhotoItems) .call(drawPhotoTypeItems) - .call(drawDateSlider) .call(drawDateFilter) .call(drawUsernameFilter) .call(drawLocalPhotos); @@ -258,166 +257,6 @@ export function uiSectionPhotoOverlays(context) { .classed('active', filterEnabled); } - function drawMaxAgeFilter(selection){ - function filterEnabled(d) { - return context.photos().maxPhotoAge(d); - } - - var ul = selection - .selectAll('.layer-list-date-age') - .data([0]); - - ul.exit() - .remove(); - - ul = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-date-age') - .merge(ul); - - var li = ul.selectAll('.list-item-date-age') - .data(context.photos().shouldFilterByMaxAge() ? ['max-age'] : []); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', 'list-item-date-age'); - - var labelEnter = liEnter - .append('label') - .each(function() { - d3_select(this) - .call(uiTooltip() - .title(() => t.append('photo_overlays.max_age_filter.tooltip')) - .placement('top') - ); - }); - - labelEnter - .append('span') - .call(t.append('photo_overlays.max_age_filter.title')); - - labelEnter - .append('select') - .attr('type', 'text') - .attr('class', 'list-option') - .call(utilNoAuto); - - var select = labelEnter.selectAll('.list-option'); - - select - .append('option') - .attr('value', -1) - .call(t.append('photo_overlays.max_age_filter.all')); - - select - .append('option') - .attr('value', 7) - .call(t.append('photo_overlays.max_age_filter.week')); - - select - .append('option') - .attr('value', 31) - .call(t.append('photo_overlays.max_age_filter.month')); - - select - .append('option') - .attr('value', 365) - .call(t.append('photo_overlays.max_age_filter.year')); - - select - .on('change', function() { - var value = d3_select(this).property('value'); - context.photos().setMaxPhotoAge(parseInt(value, 10)); - }); - - li - .merge(liEnter) - .classed('active', filterEnabled); - } - - function drawDateSlider(selection){ - - function filterEnabled() { - return context.photos().yearSliderValue(); - } - - var maxYear = new Date(); - maxYear = parseInt(maxYear.getFullYear(), 10); - - let yearSliderValue = context.photos().yearSliderValue(); - let currYear; - - if (yearSliderValue) currYear = yearSliderValue; - else currYear = maxYear; - - var ul = selection - .selectAll('.layer-list-date-slider') - .data([0]); - - ul.exit() - .remove(); - - ul = ul.enter() - .append('ul') - .attr('class', 'layer-list layer-list-date-slider') - .merge(ul); - - var li = ul.selectAll('.list-item-date-slider') - .data(context.photos().shouldFilterDateBySlider() ? ['date-slider'] : []); - - li.exit() - .remove(); - - var liEnter = li.enter() - .append('li') - .attr('class', 'list-item-date-slider'); - - var labelEnter = liEnter - .append('label') - .each(function() { - d3_select(this) - .call(uiTooltip() - .title(() => t.append('photo_overlays.age_slider_filter.tooltip')) - .placement('top') - ); - }); - - labelEnter - .append('span') - .call(t.append('photo_overlays.age_slider_filter.title')); - - let sliderWrap = labelEnter - .append('div') - .attr('class','slider-wrap'); - - let output = sliderWrap - .append('output') - .attr('class','year-selected') - .html(currYear + ' - ' + maxYear); - - sliderWrap - .append('input') - .attr('type', 'range') - .attr('max', maxYear) - .attr('list', 'dateValues') - .attr('class', 'list-option-date-slider') - .call(utilNoAuto) - .on('change', function() { - let value = parseInt(d3_select(this).property('value'), 10); - let minYear = parseInt(d3_select(this).property('min'), 10); - value = minYear + (maxYear - value); - context.photos().setFromYearFilter(value, true); - output.html(value + ' - ' + maxYear); - }); - - li - .merge(liEnter) - .classed('active', filterEnabled); - } - function drawUsernameFilter(selection) { function filterEnabled() { return context.photos().usernames(); diff --git a/test/spec/services/panoramax.js b/test/spec/services/panoramax.js new file mode 100644 index 000000000..24c7176f2 --- /dev/null +++ b/test/spec/services/panoramax.js @@ -0,0 +1,115 @@ +describe('iD.servicePanoramax', function() { + var dimensions = [64, 64]; + var context, panoramax; + + before(function() { + iD.services.panoramax = iD.servicePanoramax; + fetchMock.reset(); + }); + + after(function() { + delete iD.services.panoramax; + }); + + beforeEach(function() { + context = iD.coreContext().assetPath('../dist/').init(); + context.projection + .scale(iD.geoZoomToScale(14)) + .translate([-116508, 0]) // 10,0 + .clipExtent([[0,0], dimensions]); + + panoramax = iD.services.panoramax; + panoramax.reset(); + fetchMock.reset(); + }); + + afterEach(function() { + fetchMock.reset(); + }); + + + describe('#init', function() { + it('Initializes cache one time', function() { + var cache = panoramax.cache(); + expect(cache).to.have.property('images'); + expect(cache).to.have.property('sequences'); + + panoramax.init(); + var cache2 = panoramax.cache(); + expect(cache).to.equal(cache2); + }); + }); + + describe('#reset', function() { + it('resets cache and image', function() { + panoramax.cache().foo = 'bar'; + panoramax.setActiveImage(context, {key: 'baz'}); + + panoramax.reset(); + expect(panoramax.cache()).to.not.have.property('foo'); + expect(panoramax.getActiveImage()).to.be.null; + }); + }); + + describe('#images', function() { + it('returns images in the visible map area', function() { + var features = [ + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '0', loc: [10,0], heading: 90, sequence_id: '100', account_id: '0' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '1', loc: [10,0], heading: 90, sequence_id: '100', account_id: '1' } }, + { minX: 10, minY: 1, maxX: 10, maxY: 1, data: { id: '2', loc: [10,1], heading: 90, sequence_id: '100', account_id: '2' } } + ]; + + panoramax.cache().images.rtree.load(features); + var res = panoramax.images(context.projection); + + expect(res).to.deep.eql([ + { id: '0', loc: [10,0], heading: 90, sequence_id: '100', account_id: '0' }, + { id: '1', loc: [10,0], heading: 90, sequence_id: '100', account_id: '1' } + ]); + }); + + it('limits results no more than 5 stacked images in one spot', function() { + var features = [ + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '0', loc: [10,0], heading: 90, sequence_id: '100', account_id: '0' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '1', loc: [10,0], heading: 90, sequence_id: '100', account_id: '1' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '2', loc: [10,0], heading: 90, sequence_id: '100', account_id: '2' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '3', loc: [10,0], heading: 90, sequence_id: '100', account_id: '3' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '4', loc: [10,0], heading: 90, sequence_id: '100', account_id: '4' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '5', loc: [10,0], heading: 90, sequence_id: '100', account_id: '5' } } + ]; + + panoramax.cache().images.rtree.load(features); + var res = panoramax.images(context.projection); + expect(res).to.have.length.of.at.most(5); + }); + }); + + + describe('#sequences', function() { + it('returns sequence linestrings in the visible map area', function() { + var features = [ + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '0', loc: [10,0], heading: 90, sequence_id: '100', account_id: '0' } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { id: '1', loc: [10,0], heading: 90, sequence_id: '100', account_id: '1' } }, + { minX: 10, minY: 1, maxX: 10, maxY: 1, data: { id: '2', loc: [10,1], heading: 90, sequence_id: '100', account_id: '2' } } + ]; + + panoramax.cache().images.rtree.load(features); + panoramax.cache().sequences.lineString['100'] = { rotation: 0, images: [ features[0].data, features[1].data, features[2].data ] }; + + var res = panoramax.sequences(context.projection, 14); + expect(res).to.deep.eql([{ + rotation: 0, images: [features[0].data, features[1].data, features[2].data] + }]); + }); + }); + + describe('#selectedImage', function() { + it('sets and gets selected image', function() { + var d = { id: 'foo', sequence_id: '100'}; + panoramax.cache().images = { forImageId: { foo: d }}; + panoramax.selectImage(context, 'foo'); + expect(panoramax.getActiveImage()).to.eql(d); + }); + }); + +}); diff --git a/test/spec/svg/layers.js b/test/spec/svg/layers.js index 6440d8b2b..4d600eb9c 100644 --- a/test/spec/svg/layers.js +++ b/test/spec/svg/layers.js @@ -26,7 +26,7 @@ describe('iD.svgLayers', function () { it('creates default data layers', function () { container.call(iD.svgLayers(projection, context)); var nodes = container.selectAll('svg .data-layer').nodes(); - expect(nodes.length).to.eql(18); + expect(nodes.length).to.eql(19); expect(d3.select(nodes[0]).classed('osm')).to.be.true; expect(d3.select(nodes[1]).classed('notes')).to.be.true; expect(d3.select(nodes[2]).classed('data')).to.be.true; @@ -41,10 +41,11 @@ describe('iD.svgLayers', function () { expect(d3.select(nodes[11]).classed('kartaview')).to.be.true; expect(d3.select(nodes[12]).classed('mapilio')).to.be.true; expect(d3.select(nodes[13]).classed('vegbilder')).to.be.true; - expect(d3.select(nodes[14]).classed('local-photos')).to.be.true; - expect(d3.select(nodes[15]).classed('debug')).to.be.true; - expect(d3.select(nodes[16]).classed('geolocate')).to.be.true; - expect(d3.select(nodes[17]).classed('touch')).to.be.true; + expect(d3.select(nodes[14]).classed('panoramax')).to.be.true; + expect(d3.select(nodes[15]).classed('local-photos')).to.be.true; + expect(d3.select(nodes[16]).classed('debug')).to.be.true; + expect(d3.select(nodes[17]).classed('geolocate')).to.be.true; + expect(d3.select(nodes[18]).classed('touch')).to.be.true; }); });