diff --git a/Gemfile.lock b/Gemfile.lock index 2bf38bc8..32e314e5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -134,7 +134,7 @@ GEM minitest (5.10.1) multi_json (1.12.1) nio4r (1.2.1) - nokogiri (1.7.0.1) + nokogiri (1.7.1) mini_portile2 (~> 2.1.0) parser (2.3.3.1) ast (~> 2.2) @@ -205,7 +205,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) - rubyzip (1.2.0) + rubyzip (1.2.1) sass (3.4.23) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -303,4 +303,4 @@ DEPENDENCIES web-console BUNDLED WITH - 1.13.6 + 1.14.6 diff --git a/app/assets/images/help/additional-details-multiple.png b/app/assets/images/help/additional-details-multiple.png new file mode 100644 index 00000000..0253dfc8 Binary files /dev/null and b/app/assets/images/help/additional-details-multiple.png differ diff --git a/app/assets/images/help/base-map-toggle.png b/app/assets/images/help/base-map-toggle.png new file mode 100644 index 00000000..defc86d6 Binary files /dev/null and b/app/assets/images/help/base-map-toggle.png differ diff --git a/app/assets/images/help/brac-details.png b/app/assets/images/help/brac-details.png new file mode 100644 index 00000000..075154c3 Binary files /dev/null and b/app/assets/images/help/brac-details.png differ diff --git a/app/assets/images/help/clear-search.png b/app/assets/images/help/clear-search.png new file mode 100644 index 00000000..22aa4802 Binary files /dev/null and b/app/assets/images/help/clear-search.png differ diff --git a/app/assets/images/help/error-message.png b/app/assets/images/help/error-message.png new file mode 100644 index 00000000..a163efd6 Binary files /dev/null and b/app/assets/images/help/error-message.png differ diff --git a/app/assets/images/help/geolocation.png b/app/assets/images/help/geolocation.png new file mode 100644 index 00000000..0128975d Binary files /dev/null and b/app/assets/images/help/geolocation.png differ diff --git a/app/assets/images/help/legend.png b/app/assets/images/help/legend.png new file mode 100644 index 00000000..9d90e0a8 Binary files /dev/null and b/app/assets/images/help/legend.png differ diff --git a/app/assets/images/help/pegman.png b/app/assets/images/help/pegman.png new file mode 100644 index 00000000..5fc2a4b7 Binary files /dev/null and b/app/assets/images/help/pegman.png differ diff --git a/app/assets/images/help/printable-version.png b/app/assets/images/help/printable-version.png new file mode 100644 index 00000000..3463edd2 Binary files /dev/null and b/app/assets/images/help/printable-version.png differ diff --git a/app/assets/images/help/qualified-with-expiration.png b/app/assets/images/help/qualified-with-expiration.png new file mode 100644 index 00000000..15b0c00e Binary files /dev/null and b/app/assets/images/help/qualified-with-expiration.png differ diff --git a/app/assets/images/help/search-bar.png b/app/assets/images/help/search-bar.png new file mode 100644 index 00000000..cf8802db Binary files /dev/null and b/app/assets/images/help/search-bar.png differ diff --git a/app/assets/images/help/search-icon.png b/app/assets/images/help/search-icon.png new file mode 100644 index 00000000..f9bc4dc1 Binary files /dev/null and b/app/assets/images/help/search-icon.png differ diff --git a/app/assets/images/help/search-query.png b/app/assets/images/help/search-query.png new file mode 100644 index 00000000..ac8514d5 Binary files /dev/null and b/app/assets/images/help/search-query.png differ diff --git a/app/assets/images/help/search-results.png b/app/assets/images/help/search-results.png new file mode 100644 index 00000000..2258423d Binary files /dev/null and b/app/assets/images/help/search-results.png differ diff --git a/app/assets/images/help/zoom-control.png b/app/assets/images/help/zoom-control.png new file mode 100644 index 00000000..2967f04b Binary files /dev/null and b/app/assets/images/help/zoom-control.png differ diff --git a/app/assets/javascripts/hzmap/hz-query.js b/app/assets/javascripts/hzmap/hz-query.js index c1f78769..b6e6cd65 100644 --- a/app/assets/javascripts/hzmap/hz-query.js +++ b/app/assets/javascripts/hzmap/hz-query.js @@ -24,31 +24,31 @@ HZApp.HZQuery = { }, handleBadResponses: function(responseStatus){ if (responseStatus === 'ZERO_RESULTS' || responseStatus === 'INVALID_REQUEST'){ - $('.sidebar-card.map-report').hide(); + $('.sidebar-card.map-actions').hide(); $('#sidebar-content').addClass('zero-results'); $('#legend').addClass('zero-results'); } else { - $('.sidebar-card.map-report').show(); + $('.sidebar-card.map-actions').show(); $('#sidebar-content').removeClass('zero-results'); $('#legend').removeClass('zero-results'); } }, parseResponseGeometry: function(response){ - if (HZApp.HZQuery.response.geometry){ + if (response.geometry){ HZApp.MapUtils.jumpToLocation({ viewport: response.geometry.viewport, location: response.geometry.location }); - this.response.geocodeLocation = response.geometry.location; - if (response.place_id){ this.query.q = response.formatted_address; this.query.latlng = null; } else { this.query.q = null; - this.query.latlng = [response.geocodeLocation.lat, response.geocodeLocation.lng ].join(','); + this.query.latlng = [response.geometry.location.lat, response.geometry.location.lng ].join(','); } + + this.response.geocodeLocation = response.geometry.location; } }, updateMap: function(){ diff --git a/app/assets/javascripts/hzmap/layer-defs.js b/app/assets/javascripts/hzmap/layer-defs.js index c853e62b..2a51af8f 100644 --- a/app/assets/javascripts/hzmap/layer-defs.js +++ b/app/assets/javascripts/hzmap/layer-defs.js @@ -37,6 +37,12 @@ HZApp.Layers.LayerDefs = (function(){ layerGroup: 'qct', isVisible: true, overlay:[], + }, + qda_lg: { + layerIndex: 5, + layerGroup: 'qda', + isVisible: true, + overlay:[], } } }; diff --git a/app/assets/javascripts/hzmap/legend-defs.js b/app/assets/javascripts/hzmap/legend-defs.js index d8fb4a70..cd5e28ae 100644 --- a/app/assets/javascripts/hzmap/legend-defs.js +++ b/app/assets/javascripts/hzmap/legend-defs.js @@ -4,131 +4,39 @@ // order in this object defines draw order on the map: // first object is drawn first, then next on top of that, etc. HZApp.Legend.LegendDefs = (function(){ - var legendDefaults = { - circleFillColor: '#FFFFFF', - circleFillOpacity: 0.5, - circleStrokeColor: '#CCCCCC', - circleStrokeOpacity: 1, - circleStrokeWidth: 1, - displacementX: 0, - displacementY: 0, - fillColor: '#CCCCCC', - fillOpacity: 0.5, - graphicSpacing: 10, - lineStrokeColor: '#fff', - lineStrokeOpacity: 1, - lineStrokeWidth: 1, - lineRotation: 0, - strokeColor: '#CCCCCC', - strokeOpacity: 1, - strokeWidth: 1.25, - tileSize: 10, - - // // USWDS Alt 1 - // qctColor: '#2E8540', - // qnmcColor: '#0071BB', - // indianLandsColor: '#4C2C92' - - // Tyler 1 - qctColor: '#0D465C', - qnmcColor: '#BA233F', - indianLandsColor: '#009DCD' - }; - var legendKeys = { qct: { title: "Census Tract", svg: [], canToggle: true, - layerGroup: 'qct', - styleOptions: [ - { - type: 'polygon', - fillColor: legendDefaults.qctColor, - fillOpacity: legendDefaults.fillOpacity, - strokeColor: legendDefaults.qctColor, - strokeOpacity: legendDefaults.strokeOpacity, - strokeWidth: legendDefaults.strokeWidth - } - ] + layerGroup: 'qct' }, qnmc: { title: "County", svg: [], canToggle: true, - layerGroup: 'qnmc', - styleOptions: [ - { - type: 'polygon', - fillColor: legendDefaults.qnmcColor, - fillOpacity: legendDefaults.fillOpacity, - strokeColor: legendDefaults.qnmcColors, - strokeOpacity: legendDefaults.strokeOpacity, - strokeWidth: legendDefaults.strokeWidth - } - ] + layerGroup: 'qnmc' }, indian_lands: { title: "Indian Land", svg: [], canToggle: true, - layerGroup: 'indian_lands', - styleOptions: [ - { - type: 'polygon', - fillColor: legendDefaults.indianLandsColor, - fillOpacity: legendDefaults.fillOpacity, - strokeColor: legendDefaults.indianLandsColor, - strokeOpacity: legendDefaults.strokeOpacity, - strokeWidth: legendDefaults.strokeWidth - } - ] + layerGroup: 'indian_lands' }, redesignated: { title: "Redesignated", svg: [], canToggle: true, - layerGroup: 'redesignated', - styleOptions: [ - { - type: 'horline', - lineStrokeColor: legendDefaults.lineStrokeColor, - lineStrokeWidth: 5, - lineStrokeOpacity: legendDefaults.fillOpacity, - strokeWidth: legendDefaults.strokeWidth, - strokeColor: legendDefaults.lineStrokeColor, - strokeOpacity: legendDefaults.strokeOpacity, - tileSize: 30, - lineRotation: 0 - } - ] + layerGroup: 'redesignated' }, brac: { title: "Base Closure Area", svg: [], canToggle: true, - layerGroup: 'brac', - styleOptions: [ - { - type: 'circle', - circleFillColor: '#fff', - circleFillOpacity: legendDefaults.fillOpacity, - circleStrokeColor: '#fff', - circleStrokeOpacity: legendDefaults.strokeOpacity, - circleStrokeWidth: legendDefaults.strokeWidth, - strokeColor: '#000000', - strokeOpacity: legendDefaults.strokeOpacity, - strokeWidth: legendDefaults.strokeWidth, - tileSize: 15, - graphicSpacing: legendDefaults.graphicSpacing - } - ] + layerGroup: 'brac' }, }; - return { - legendDefaults: legendDefaults, legend: legendKeys }; - })(); diff --git a/app/assets/javascripts/hzmap/legend.js.erb b/app/assets/javascripts/hzmap/legend.js.erb index 49ca049f..4941c317 100644 --- a/app/assets/javascripts/hzmap/legend.js.erb +++ b/app/assets/javascripts/hzmap/legend.js.erb @@ -2,147 +2,38 @@ HZApp.Legend = (function(){ return { legend: HZApp.Legend.LegendDefs.legend, - buildLegend: function(layers){ - Object.keys(layers).map(function(layer){ - var legendConfig = HZApp.Legend.getConfigFromLayerStyle(layers[layer]); - HZApp.Legend.legend[legendConfig.layerGroup].svg.push(HZApp.Legend.svgFromStyle(legendConfig)); - }); - - Object.keys(this.legend).map(HZApp.Legend.insertLegendItem); - + buildLegend: function(){ this.addLegendButtonListeners(); - - this.setLegendState(window.innerWidth); - + this.setMobileState(window.innerWidth); this.addLayerToggleListeners(); - }, addLegendButtonListeners: function(){ - $('#legend-header').click(function(event) { + $('.legend-header').click(function(event) { HZApp.Legend.toggleLegendVisibility(event.currentTarget.className); }); }, toggleLegendVisibility: function(legendState) { - if(legendState === 'open') { + if(legendState.includes('open')) { HZApp.Legend.hideLegend(); - $('#legend-header').removeClass('open'); } else { HZApp.Legend.showLegend(); - $('#legend-header').addClass('open'); } }, - getConfigFromLayerStyle: function(layer){ - return { - layerGroup: layer.layerGroup, - styleType: layer.styleOptions[0].type, - styleColor: layer.styleOptions[0][HZApp.Legend.legendTypeToColorType[layer.styleOptions[0].type]] - }; - }, - svgFromStyle: function(style){ - var width = 34, height = 29; - var svg = this.svgHeader(width, height); - svg += 'hubzone legend'; - var svg_fn_name = "svg_" + style.styleType; - svg += HZApp.Legend[svg_fn_name](style,width, height); - svg += ''; - return svg; - }, - svgHeader: function(width, height){ - return ('' - ); - }, - svg_polygon: function(style, width, height){ - return ( '' + - '' + - '' - ); - }, - svg_horline: function(style, width, height){ - return ( - // '' + - '' + - '' + - '' + - '' + - // '' + - '' + - '' + - '' + - '' + - '' - ); - }, - svg_circle: function(style, width, height){ - return ( - // '' + - '' + - '' + - '' + - '' + - // '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' - ); - }, - insertLegendItem: function(legendItem) { - HZApp.Legend.legend[legendItem].canToggle ? HZApp.Legend.insertLegendItemToggle(legendItem) : HZApp.Legend.insertLegendItemNoToggle(legendItem); - }, - insertLegendItemToggle: function(legendItem){ - var li = '
  • '; - li += ''; - li += ''; - li += '
  • '; - $('#legend > ul').append(li); - }, - // insertLegendItemNoToggle: function(legendItem){ - // var li = '
  • '; - // HZApp.Legend.legend[legendItem].svg.map(function(svg){ li += svg; }); - // li += '' + I18n.t('legend.' + legendItem) + ''; - // li += '
  • '; - // $('#legend > ul').append(li); - // }, - legendTypeToColorType: { //mapping style type to css/svg color parameter - 'polygon': 'fillColor', - 'circle': 'circleFillColor', - 'horline': 'lineStrokeColor' - }, hideLegend: function(){ - $('#legend li.legend-item').hide(); - $('#hide-legend-button').hide(); - $('#legend-header-title-expanded').hide(); - $('#legend-header-title-hidden').show(); - $('#show-legend-button').show(); + $('.legend-item').hide(); + $('.legend-button').addClass("fa-chevron-up").removeClass("fa-chevron-down"); + $('.legend-content').hide(); + $('.legend-header').removeClass('open'); }, showLegend: function(){ - $('#legend li.legend-item').show(); - $('#hide-legend-button').show(); - $('#legend-header-title-expanded').show(); - $('#legend-header-title-hidden').hide(); - $('#show-legend-button').hide(); - }, - setLegendState: function(windowWidth) { - if (windowWidth > 950) { - HZApp.Legend.toggleLegendVisibility(''); - } else { - HZApp.Legend.toggleLegendVisibility('open'); + $('.legend-item').show(); + $('.legend-button').addClass("fa-chevron-down").removeClass("fa-chevron-up"); + $('.legend-content').show(); + $('.legend-header').addClass('open'); + }, + setMobileState: function(windowWidth) { + if (windowWidth < 950) { + HZApp.Legend.hideLegend(); } }, addLayerToggleListeners: function() { diff --git a/app/assets/javascripts/hzmap/map-utils.js.erb b/app/assets/javascripts/hzmap/map-utils.js.erb index 16ea6fba..6539aab3 100644 --- a/app/assets/javascripts/hzmap/map-utils.js.erb +++ b/app/assets/javascripts/hzmap/map-utils.js.erb @@ -1,4 +1,5 @@ HZApp.MapUtils = (function(){ + return { // turn latlng object into url catchMapClick: function(clickEvent){ @@ -26,20 +27,111 @@ HZApp.MapUtils = (function(){ //jump to location on the map based on the geocode viewport object jumpToLocation: function(geocodeLocation){ if (geocodeLocation.viewport){ - var newBounds = this.createGoogleLatLngBounds( - geocodeLocation.viewport.southwest.lng, - geocodeLocation.viewport.southwest.lat, - geocodeLocation.viewport.northeast.lng, - geocodeLocation.viewport.northeast.lat - ); - HZApp.map.fitBounds(newBounds); + var customFit = this.getBoundsZoomLevel(geocodeLocation.viewport, this.getMapDims($('#map'))); + if (customFit.fitZoom < 15){ + var newBounds = this.createGoogleLatLngBounds( + geocodeLocation.viewport.southwest.lng, + geocodeLocation.viewport.southwest.lat, + geocodeLocation.viewport.northeast.lng, + geocodeLocation.viewport.northeast.lat + ); + HZApp.map.fitBounds(newBounds); + } else { + this.panAndZoom(customFit.center, 15); + } } }, + getMapDims: function($el){ + return { + height: $el.height(), + width: $el.width() + }; + }, + panAndZoom: function(center, zoom){ + HZApp.map.setZoom(zoom); + HZApp.map.setCenter(center); + }, + getBoundsZoomLevel: function(viewport, mapDims) { + var WORLD_DIM = { height: 256, width: 256 }; + var ZOOM_MAX = 21; + + function latRad(lat) { + var sin = Math.sin(lat * Math.PI / 180); + var radX2 = Math.log((1 + sin) / (1 - sin)) / 2; + return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; + } + + function zoom(mapPx, worldPx, fraction) { + return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2); + } + var ne = viewport.northeast; + var sw = viewport.southwest; + + var latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI; + + var lngDiff = ne.lng - sw.lng; + var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; + + var latZoom = zoom(mapDims.height, WORLD_DIM.height, latFraction); + var lngZoom = zoom(mapDims.width, WORLD_DIM.width, lngFraction); + + var latAvg = (ne.lat + sw.lat) / 2.0; + var lngAvg = (ne.lng + sw.lng) / 2.0; + + return { + fitZoom: Math.min(latZoom, lngZoom, ZOOM_MAX), + center: new google.maps.LatLng(latAvg, lngAvg) + }; + }, createGoogleLatLngBounds: function(SWLng, SWLat, NELng, NELat){ return new google.maps.LatLngBounds( new google.maps.LatLng(SWLat, SWLng), new google.maps.LatLng(NELat, NELng) ); }, + mapNameUpdates: [ + {mapType:'roadmap', newName: 'Standard'}, + {mapType:'hz_map', newName: 'Light Gray'}, + {mapType:'hybrid', newName: 'Satellite'} + ], + updateMapTypeNames: function(){ + HZApp.MapUtils.mapNameUpdates.map(HZApp.MapUtils.customMapTypeName); + HZApp.map.setOptions({'mapTypeControl':true}); + }, + customMapTypeName: function(mapNameUpdate){ + if (!HZApp.map.mapTypes[mapNameUpdate.mapType]) { return; } + HZApp.map.mapTypes[mapNameUpdate.mapType].name = mapNameUpdate.newName; + }, + + /* + This is a convenience method for helping to parse the base map style object at HZApp.Styles.hzBaseMapStyle + to the format required by the Google Maps Static API (https://developers.google.com/maps/documentation/static-maps/styling, 2017-05-05) + so that the report map style can match the web map style. + It is intended that this method would only need to be called manually to produce the style string which could then be included in the + Report repo directly within the wms_utl.rb class at hz_base_map_style + */ + parseStyleToStaticUrl: function(styles){ + styles = styles || HZApp.Styles.hzBaseMapStyle; + var stylesOut = []; + + styles.map( function(style){ + var styleStr = '&style='; + + styleStr += 'feature:' + style.featureType + '|'; + styleStr += 'element:' + style.elementType + '|'; + + var styleTags = []; + style.stylers.map( function(s) { + var s_key = Object.keys(s)[0]; + styleTags.push(s_key + ':' + s[s_key]); + }); + + styleStr += styleTags.join('|'); + styleStr = styleStr.replace(/\#/g, '0x'); + stylesOut.push(styleStr); + }); + + return encodeURI(stylesOut.join('&')); + } }; })(); diff --git a/app/assets/javascripts/hzmap/map.js.erb b/app/assets/javascripts/hzmap/map.js.erb index 305d049d..47ced47e 100644 --- a/app/assets/javascripts/hzmap/map.js.erb +++ b/app/assets/javascripts/hzmap/map.js.erb @@ -12,12 +12,20 @@ function initMap() { streetViewControlOptions: { position: google.maps.ControlPosition.RIGHT_BOTTOM }, + mapTypeId: 'hz_map', mapTypeControlOptions: { - mapTypeIds: ['roadmap', 'hz_map', 'hybrid' ], - position: google.maps.ControlPosition.BOTTOM_CENTER - } + mapTypeIds: ['hz_map','roadmap', 'hybrid' ], + position: google.maps.ControlPosition.BOTTOM_CENTER, + style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR + }, }); + + //reset mapType names + //refer to maptype spec: https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapType + HZApp.map.setOptions({'mapTypeControl':false}); + google.maps.event.addListenerOnce(HZApp.map, 'idle', HZApp.MapUtils.updateMapTypeNames); + //adds in the hz style into the basemap picker var hzStyledMap = new google.maps.StyledMapType(HZApp.Styles.hzBaseMapStyle, {name: 'Gray'}); HZApp.map.mapTypes.set('hz_map', hzStyledMap); @@ -29,7 +37,7 @@ function initMap() { //add in the WMS tiles HZApp.WMTSUtils.initializeTiles(); - //add listener on the map for clciks + //add listener on the map for clciks HZApp.WMTSUtils.addClickListeners(HZApp.map); //adds autocomplete and click listener diff --git a/app/assets/javascripts/hzmap/sidebar.js b/app/assets/javascripts/hzmap/sidebar.js index df5af5c0..bd1c1904 100644 --- a/app/assets/javascripts/hzmap/sidebar.js +++ b/app/assets/javascripts/hzmap/sidebar.js @@ -1,27 +1,26 @@ //Sidebar utils -HZApp.SidebarUtils = (function(){ +HZApp.SidebarUtils = (function(){ // extend jquery with our sidebar function $.fn.sidebar = function() { var $sidebar = this; - $sidebar.currentClass = 'hidden'; $sidebar.update = function() { if (!$sidebar.hasClass('on')) { $sidebar.addClass('on'); $('#legend').addClass('legend-mobile'); $sidebar.removeClass('hidden'); + $sidebar.removeClass('closed'); $('#sidebar-button').html(''); $('div.gmnoprint[controlheight="55"], div.gmnoprint[controlheight="66"], .gm-svpc').addClass('gm-sidebar-on'); $('#geolocation').addClass('geolocation-sidebar-on'); - $sidebar.currentClass = 'on'; } else { $sidebar.removeClass('on'); $('#legend').removeClass('legend-mobile'); + $sidebar.addClass('closed'); $('#sidebar-button').html(''); $('div.gmnoprint[controlheight="55"], div.gmnoprint[controlheight="66"], .gm-svpc').removeClass('gm-sidebar-on'); $('#geolocation').removeClass('geolocation-sidebar-on'); - $sidebar.currentClass = 'hidden'; $('#hubzone-qualifications').attr("aria-live", "off"); } }; @@ -44,7 +43,7 @@ HZApp.SidebarUtils = (function(){ triggerSidebar: function(){ $('#sidebar').hasClass('on') ? HZApp.GA.track( 'map', 'sidebar', 'hide' ) : HZApp.GA.track( 'map', 'sidebar', 'show' ); - $('#sidebar').hasClass('on') ? HZApp.SidebarUtils.sidebar.close() : HZApp.SidebarUtils.sidebar.open(); + $('#sidebar').hasClass('on') ? HZApp.SidebarUtils.sidebar.close() : HZApp.SidebarUtils.sidebar.open(); }, sidebar: {}, buildSidebar: function(){ @@ -59,4 +58,3 @@ HZApp.SidebarUtils = (function(){ } }; })(); - diff --git a/app/assets/javascripts/hzmap/wmts-utils.js.erb b/app/assets/javascripts/hzmap/wmts-utils.js.erb index 5208c923..0a820a8b 100644 --- a/app/assets/javascripts/hzmap/wmts-utils.js.erb +++ b/app/assets/javascripts/hzmap/wmts-utils.js.erb @@ -1,9 +1,9 @@ // web map tile service utilites HZApp.WMTSUtils = (function(){ - - Math.sinh = Math.sinh || function(angle){ + + Math.sinh = Math.sinh || function(angle){ return (Math.exp(angle) - Math.exp(-angle)) / 2; - }; + }; return { tileSize: 256, @@ -31,7 +31,28 @@ HZApp.WMTSUtils = (function(){ HZApp.Layers.LayerDefs.hzWMSOverlays[layer].overlay = customMapType; HZApp.map.overlayMapTypes.insertAt(layerIndex, customMapType); }, + // Normalizes the coords that tiles repeat across the x axis (horizontally) + // like the standard Google map tiles. + getNormalizedCoord: function(coord, zoom) { + var y = coord.y; + var x = coord.x; + // tile range in one direction range is dependent on zoom level + // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc + var tileRange = 1 << zoom; + + if (y < 0 || y >= tileRange) { // don't repeat across y-axis (vertically) + y = y; + } + + if (x < 0 || x >= tileRange) { // repeat across x-axis + x = (x % tileRange + tileRange) % tileRange; + } + return {x: x, y: y}; + }, tile2Bbox: function(x, y, zoom){ + var normCoord = this.getNormalizedCoord({x: x, y: y}, zoom); + x = normCoord.x; + y = normCoord.y; var bb = {}; bb.n = this.tile2Lat(y,zoom); bb.s = this.tile2Lat(y+1, zoom); @@ -43,21 +64,17 @@ HZApp.WMTSUtils = (function(){ return [webMSW[0], webMSW[1], webMNE[0], webMNE[1]].join(','); }, toWebMercator: function(x_lon, y_lat){ - if ((Math.abs(x_lon) > 180) || (Math.abs(y_lat) > 90)){ - return []; - } else { - var semimajor_axis = 6378137.0; - var east = x_lon * 0.017453292519943295; - var north = y_lat * 0.017453292519943295; - var northing = 3189068.5 * Math.log((1.0 + Math.sin(north)) / (1.0 - Math.sin(north))); - var easting = semimajor_axis * east; - return [easting, northing]; - } + var semimajor_axis = 6378137.0; + var east = x_lon * 0.017453292519943295; + var north = y_lat * 0.017453292519943295; + var northing = 3189068.5 * Math.log((1.0 + Math.sin(north)) / (1.0 - Math.sin(north))); + var easting = semimajor_axis * east; + return [easting, northing]; }, tile2Lng: function(x, z){ return (x / Math.pow(2.0, z) * 360.0 - 180); }, - tile2Lat: function(y,z) { + tile2Lat: function(y,z) { var n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z); return this.toDegrees(Math.atan(Math.sinh(n))); }, @@ -74,11 +91,12 @@ HZApp.WMTSUtils = (function(){ url += "&VERSION=1.1.1"; url += "&REQUEST=GetMap"; url += "&SRS=EPSG:900913"; - url += "&bbox=" + this.tile2Bbox(coord.x, coord.y, zoom); + // url += "&SRS=EPSG:4326"; + url += "&bbox=" + this.tile2Bbox(coord.x, coord.y, zoom); url += "&width=" + this.tileSize; url += "&height=" + this.tileSize; url += "&format=image/png"; - return url; + return url; }, addClickListeners: function(overlay) { overlay.addListener('click', this.handleSingleClick); diff --git a/app/assets/stylesheets/_globals.scss b/app/assets/stylesheets/_globals.scss index f4d336fd..2724ccfa 100644 --- a/app/assets/stylesheets/_globals.scss +++ b/app/assets/stylesheets/_globals.scss @@ -1,3 +1,3 @@ .hidden { - display: none; + display: none !important; } diff --git a/app/assets/stylesheets/_layers.scss b/app/assets/stylesheets/_layers.scss new file mode 100644 index 00000000..65cd1a4f --- /dev/null +++ b/app/assets/stylesheets/_layers.scss @@ -0,0 +1,60 @@ +@import "variables"; + +.layer-qct { + background-color: #0D465C; +} +.layer-qct_e { + background-color: #0D465C; +} +.layer-qct_brac { + background-color: #ffffff; + background-image: url("data:image/svg+xml;utf8,"); +} +.layer-qct_r { + background-color: #ffffff; + background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%230D465C' fill-opacity='1' fill-rule='evenodd'%3E%3Cpath d='M0 40L40 0H20L0 20M40 40V20L20 40'/%3E%3C/g%3E%3C/svg%3E"); +} +.layer-qnmc { + background-color: #BA233F; +} +.layer-qnmc_a { + background-color: #BA233F; +} +.layer-qnmc_ab { + background-color: #BA233F; +} +.layer-qnmc_b { + background-color: #BA233F; +} +.layer-qnmc_c { + background-color: #BA233F; +} +.layer-qnmc_dda { + background-color: #BA233F; +} +.layer-qnmc_e { + background-color: #BA233F; +} +.layer-qnmc_r { + background-color: #ffffff; + background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23BA233F' fill-opacity='1' fill-rule='evenodd'%3E%3Cpath d='M0 40L40 0H20L0 20M40 40V20L20 40'/%3E%3C/g%3E%3C/svg%3E"); +} +.layer-qnmc_brac { + background-color: #ffffff; + background-image: url("data:image/svg+xml;utf8,"); +} +.layer-indian_lands { + background-color: #009DCD; +} +.layer-brac { + background-color: #ffffff; + background-image: url("data:image/svg+xml;utf8,"); +} +.layer-qct_qda { + background-color: #ffffff; + background-image: url("data:image/svg+xml;utf8,"); +} +.layer-qnmc_qda { + background-color: #ffffff; + background-image: url("data:image/svg+xml;utf8,"); +} diff --git a/app/assets/stylesheets/_map_body.scss b/app/assets/stylesheets/_map_body.scss index d3dee09d..57fe91bf 100644 --- a/app/assets/stylesheets/_map_body.scss +++ b/app/assets/stylesheets/_map_body.scss @@ -2,7 +2,10 @@ .map-body { position: absolute; width: 100%; - height: calc(100% - 87px); + height: calc(100% - 80px); +} +.map-body:focus { + outline: none; } @media only screen and (max-width: 950px) { .map-body { diff --git a/app/assets/stylesheets/_map_header.scss b/app/assets/stylesheets/_map_header.scss index 239d9cab..70fce993 100644 --- a/app/assets/stylesheets/_map_header.scss +++ b/app/assets/stylesheets/_map_header.scss @@ -10,7 +10,7 @@ } .usa-logo-text a > h1 { font-family: $font-sans; - font-size: 3.6rem; + font-size: 3.5rem; font-weight: 700; color: $gray-dark; float: left; @@ -20,7 +20,7 @@ } .usa-nav-secondary { position: fixed; - top: -4.5rem; + top: -4.3rem; right: 2rem; } .usa-nav-secondary-links li:not(:last-child)::after { @@ -31,18 +31,18 @@ } .usa-banner-inner { max-width: 100%; - padding: 0 2rem 0 2rem; + padding-left: 1.5rem; } .usa-header-extended .usa-logo { - margin-top: 1rem; - margin-bottom: 1rem; + margin-top: .7rem; + margin-bottom: .7rem; } .usa-header-extended .usa-nav { border-top: none; } .usa-header-extended .usa-navbar { max-width: 100%; - padding: 0 2rem 0 2rem; + padding-left: 1.5rem; border-bottom: 2px solid $gray-lighter; } @media only screen and (max-width: 950px) { @@ -56,9 +56,17 @@ .usa-navbar { border-bottom: none; } + .usa-nav-secondary { + position: fixed; + top: inherit; + right: inherit; + } .usa-banner { display: none; } + .usa-menu-btn { + display: block; + } .usa-logo { margin-left: .5rem; } @@ -76,6 +84,9 @@ } } @media only screen and (max-width: 480px) { + .usa-unstyled-list > li { + margin: 1em 0; + } .usa-banner { display: none; } diff --git a/app/assets/stylesheets/_map_legend.scss b/app/assets/stylesheets/_map_legend.scss index 3c35b0e3..26bc614d 100644 --- a/app/assets/stylesheets/_map_legend.scss +++ b/app/assets/stylesheets/_map_legend.scss @@ -1,69 +1,103 @@ @import 'variables'; +@import 'layers'; #legend { font-family: $font-sans; font-size: 1.5rem; - background-color: $white; + margin: 0 0 0 1rem; + background-color: rgba(255,255,255,0.97); + border-radius: 3px; -webkit-box-shadow: 0px 1px 4px -1px rgba(0,0,0,0.29); -moz-box-shadow: 0px 1px 4px -1px rgba(0,0,0,0.29); box-shadow: 0px 1px 4px -1px rgba(0,0,0,0.29); - border-radius: 3px; - margin: 0 0 0 1rem; - padding: 1rem; } -#legend label { - margin-top: 0.3rem; +.legend-header { + cursor: pointer; + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0.8rem 1rem; } -#legend ul { - list-style-type: none; - padding-left: 0; +.legend-header h4 { margin: 0; + padding: 0; + color: $gray-dark; } -#legend li svg:not(:root) { - margin-bottom: -0.9rem; +.legend-button { + float: right; + color: $gray-dark; + margin-left: 0.5rem; + margin-top: 0.1rem; + font-size: 1.7rem; } -#legend-header { - margin-bottom: 0; +.legend-content { display: block; - cursor: pointer; + list-style-type: none; + margin: 0; + padding: .6rem 1rem 1rem 1rem; + box-shadow: inset 0px 1px 0 0px $gray-lighter; } -.legend-title { +.legend-list-title { color: $gray-dark; - font-size: 1.8rem; - font-weight: bold; - padding-right: 0; + font-size: 1.6rem; + margin-bottom: 0.5rem; } -#legend-button-div { +.legend-item label { + font-size: 1.4rem; color: $gray-dark; - float: right; - padding-top: 0; - padding-left: .5rem; - padding-right: .5rem; - font-size: 1.7rem; + display: flex; + align-items: center; + margin-top: 0.3rem; + margin-left: 0.3rem; } -.no-toggle { - padding-left: 2.7rem; +.legend-layer-symbols { + background-size: 0.9rem; + width: 2.4rem; + height: 2.4rem; + margin-right: 0.5rem; } -[type="checkbox"]:checked + label::before, -[type="radio"]:checked + label::before { - background-color: $gray-dark; - box-shadow: 0 0 0 1px $gray-dark; +.legend-content input[type="checkbox"] { + margin-left: 0; } - -[type="radio"]:checked + label::before { - box-shadow: 0 0 0 2px #ffffff, 0 0 0 4px $gray-dark; +.legend-content input[type="checkbox"]:checked + label::before{ + background-color: $white; + box-shadow: 0 0 0 2px $gray-dark; } - -[type="checkbox"]:focus + label::before { +.legend-content input[type="checkbox"]:focus + label::before { box-shadow: 0 0 0 1px #ffffff, 0 0 0 3px $gray-dark; } @media only screen and (max-width: 950px) { + .zero-results{ + bottom: 12.5rem !important; + } .legend-mobile { - bottom: 11rem !important; + bottom: 12.5rem !important; } - - #legend.zero-results{ - bottom: 6rem !important; + .legend-content { + padding: 0.5rem 1rem; + } + .legend-header { + padding: 0.7rem 1rem; + } + .legend-header h4{ + font-size: 1.5rem; + } + .legend-button { + font-size: 1.5rem; + } + .legend-list-title { + font-size: 1.5rem; + margin-bottom: 0; + } + .legend-item { + font-size: 1.3rem; + } + .legend-item label { + margin-top: 0; + } + .legend-layer-symbols { + width: 2.25rem; + height: 2.25rem; } } diff --git a/app/assets/stylesheets/_map_search.scss b/app/assets/stylesheets/_map_search.scss index 72d74b81..6518661d 100644 --- a/app/assets/stylesheets/_map_search.scss +++ b/app/assets/stylesheets/_map_search.scss @@ -4,13 +4,13 @@ position: absolute; top: 10px; z-index: 3; - left: 1.6rem; + left: 1.2rem; } #search-field-small { background-color: $white; border: .5px solid $gray; border-right: none; - height: 4rem; + height: 3.8rem; font-size: 2rem; color: $gray-dark; padding: 1rem 2em 1rem 0.7rem; @@ -27,8 +27,8 @@ } #hubzone-search-button { background-color: $gray-dark; - background-size: 4rem; - height: 4rem; + background-size: 3.8rem; + height: 3.8rem; float: left; } #search-field-small:not(:valid) ~ .clear-search { @@ -41,7 +41,7 @@ button.clear-search { position: absolute; text-align: right; right: 7rem; - top: .8rem; + top: .7rem; color: $gray-dark; font-size: 2.4rem; } @@ -107,9 +107,6 @@ button.clear-search:hover { .pac-item { padding-left: .25rem; } - .usa-menu-btn { - display: none; - } button.clear-search { right: 5.6rem; } diff --git a/app/assets/stylesheets/_map_sidebar.scss b/app/assets/stylesheets/_map_sidebar.scss index 515c7ece..6fffed1c 100644 --- a/app/assets/stylesheets/_map_sidebar.scss +++ b/app/assets/stylesheets/_map_sidebar.scss @@ -11,6 +11,7 @@ } #sidebar.on { right: 0; + transition-duration: 0ms; } #sidebar-button { position: absolute; @@ -28,31 +29,26 @@ .sidebar-card { position: relative; width: 90%; - min-height: 4em; margin: 1rem auto 1rem auto; - padding: 1.5rem 0 1.5rem 0; background-color: $white; box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.25); border-radius: 3px; display: table; } - .hubzone-sidebar-cell { display: table-cell; + padding: 1.5rem 0 1.5rem 0; } - .hubzone-sidebar-row { display: table-row; } - .hubzone-sidebar-cell.text { padding-left: 3rem; + padding-right: 1rem; vertical-align: middle; } - .sidebar-card h2 { display: inline; - vertical-align: middle; top: 1rem; max-width: 11rem; margin: 0; @@ -62,7 +58,6 @@ font-weight: normal; color: $gray-dark; } - .sidebar-card h4 { display: inline; vertical-align: middle; @@ -86,13 +81,18 @@ font-size: 2.4rem; font-weight: normal; padding-left: 0.8rem; + padding-top: 0.3rem; +} +.hubzone-until-date { + float: left; + padding-left: 1rem; } .qualified-hubzone { background-color: $green-light; color: $white; } .non-qualified-hubzone { - background-color: $gray-dark; + background-color: $gray; color: $white; } .hubzone-status-indicator .fa.fa-times-circle-o, .fa.fa-check-circle-o { @@ -100,20 +100,20 @@ font-size: 4rem; } .sidebar-qualifications { - width: 90%; - min-width: auto; - margin: 0 auto; - border-radius: 3px; - box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.25); - border-collapse: separate; - overflow: hidden; + margin: 0; +} +.qualification-label-wrapper { + vertical-align: middle; + display: table-cell; + width: 100%; + padding-left: 1rem; } -table.sidebar-qualifications th { +table th { border: none; } .sidebar-qualifications thead th { border: none; - padding: 1rem 1.5rem 1rem 1.5rem; + border-radius: 3px; } .sidebar-qualifications tbody th { background-color: $white; @@ -122,11 +122,28 @@ table.sidebar-qualifications th { font-size: 1.5rem; font-weight: 400; } -.sidebar-qualifications th > span { +.qualification-label-wrapper > span { float: none; font-weight: bold; } -.sidebar-qualifications tfoot tr td { +.sidebar-additional-details { + margin: 0; +} +.sidebar-additional-details thead th { + border: none; + border-radius: 3px; +} +.sidebar-additional-details tbody th { + background-color: $white; + border-top: 1px solid $gray-lightest; + color: $gray-dark; + font-size: 1.5rem; + font-weight: 400; + padding: 0; + display: table; + width: 100%; +} +.additional-details tfoot tr td { border-left: 1px solid $gray-lightest; border-right: 1px solid $gray-lightest; border-bottom: 1px solid $gray-lightest; @@ -134,10 +151,9 @@ table.sidebar-qualifications th { border-radius: 0 0 3px 3px; font-size: 1.5rem; } -.hubzone-status-date { - font-weight: normal; - font-size: 1.3rem; - padding-left: 2px; +th.additional-details-title { + background-color: $white; + color: $gray-dark; } i.fa.fa-print { font-size: 2.5rem; @@ -157,8 +173,8 @@ i.fa-file-pdf-o { display: none; } button#map-report { - background: none; - margin: 0.5rem 0.5rem .5rem 1.5rem; + background-color: $white; + margin: 1.7rem 0.5rem 1.5rem 1.5rem; padding: 0rem; } .create-report { @@ -167,62 +183,102 @@ button#map-report { padding-top: 0.5rem; color: $gray-dark; } +.sidebar-footer { + width: 34rem; + position: fixed; + bottom: 0; + background-color: $white; +} +.hubzone-status-date { + position: absolute; + bottom: 0px; + padding-top: 0.4rem; + padding-bottom: 0.4rem; + margin: 1.5rem 1.5rem 0.5rem 1.5rem; + text-align: center; + font-weight: normal; + font-size: 1.3rem; + color: #5b616b; +} +.sidebar-layer-symbol { + background-repeat: repeat; + background-size: 1.75rem; + width: 4rem; + height: 6rem; + float: left; +} @media only screen and (max-width: 950px) { #sidebar-button { display: none; } #sidebar { + position: fixed; + bottom: 0; width: 100%; - height: auto; - top: 100%; - background-color: transparent; + height: 12rem; + overflow: scroll; + box-shadow: 0px 0px 3px 2px rgba(0,0,0,0.2); + background-color: rgba(255,255,255,0.85); } - - #sidebar-content.zero-results { - margin-top: 0; - position: absolute; - bottom: 0px; + .sidebar-card { + display: block; + width: calc(100% - 2rem); + margin: 0.75rem 1rem; } - - #sidebar-content { - margin-top: -100px; + .hubzone-sidebar-cell{ + padding: 0.5rem 0 0.5rem 0; } - .sidebar-card.search-result { - min-height: 5.1rem; + .hubzone-status-date { + display: none; } - .sidebar-card { - width: 100%; - border-radius: 0; - margin: 0; - padding: 0.5rem; - min-height: auto; + .sidebar-card h2 { + font-size: 1.7rem; + line-height: 0rem; } - .sidebar-qualifications { - width: 100%; - border: none; - border-radius: 0; + .hubzone-until-date { + clear: left; + margin-top: -0.8rem; + padding-top: 0; + padding-left: 3rem; } - .sidebar-card > h2 { - font-size: 1em; - width: 100%; - padding-left: 3.3rem; + .sidebar-qualifications thead th { + font-size: 1.5rem; + padding: 0.25rem 1.6rem; } - .sidebar-card .fa.fa-map-marker { - font-size: 3.5rem; + #hubzone-qualifications:focus { + outline: none; } - .sidebar-qualifications thead th { - padding: 0.5rem 1.5rem 0.5rem 2rem; + #hubzone-status { + float: left; + font-size: 1.7rem; + padding-left: 0.5rem; } .hubzone-status-indicator .fa.fa-times-circle-o, .fa.fa-check-circle-o { - font-size: 3.5rem; + font-size: 2.8rem; } - #hubzone-status { - font-size: 1em; + .sidebar-additional-details thead th { + padding: 1rem; + font-size: 1.5rem; + } + .sidebar-layer-symbol { + width: 3rem; + height: 5rem; + } + .sidebar-card .fa.fa-map-marker { + font-size: 2.8rem; + } + i.fa-file-pdf-o { + float: left; + margin-right: 1rem; } button#map-report { - text-align: left; - margin: .5rem 0 .5rem 2rem; - cursor: pointer; + background-color: $white; + float: none; + margin: 0; + padding: 1rem 0.5rem 1rem 1.5rem; + } + .create-report { + float: left; } } diff --git a/app/assets/stylesheets/help.scss b/app/assets/stylesheets/help.scss new file mode 100644 index 00000000..6cdc9a51 --- /dev/null +++ b/app/assets/stylesheets/help.scss @@ -0,0 +1,103 @@ +// Place all the styles related to the Help controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ + +@import "variables"; + +#hubzone-map-guide { + padding-top: 2rem; + padding-left: 0; + margin-left: 1.5rem; + margin-right: 1.5rem; + color: $gray-dark; +} +#hubzone-map-guide h1,h2,h3,h4,h5,h6 { + font-family: $font-sans; +} +#hubzone-map-guide p { + max-width: inherit; +} +#hubzone-map-guide ul { + list-style-type: none; +} +.usa-layout-docs { + flex-direction: inherit; +} +.usa-layout-docs-sidenav { + order: 1; + margin-top: 0; + margin-bottom: 5rem; +} +.usa-layout-docs-faq { + order: 2; +} +.usa-layout-docs-main_content { + order: 2; +} +.usa-font-lead { + font-family: $font-sans; +} +.map-guide-header { + max-width: 100%; + padding: 2rem 0; + border-bottom: 2px solid $gray-lighter; +} +.return-to-map { + margin-left: 1.5rem; +} +.return-to-map i { + margin-right: 1rem; + -moz-transform: scaleX(-1); + -o-transform: scaleX(-1); + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + filter: FlipH; + -ms-filter: "FlipH"; +} +.help-nav { + position: sticky; + position: -webkit-sticky; + top: 2rem; +} +.help-nav a:focus{ + box-shadow: none; +} +.help-layer-symbols { + background-repeat: repeat; + background-size: 1rem; + width: 3rem; + height: 3rem; + display: inline-block; + margin-right: 1rem; + vertical-align: middle; +} +.return-to-top { + position: relative; + top: 3rem; +} +@media only screen and (max-width: 950px) { + #hubzone-map-guide { + padding-top: 0; + margin-left: 1rem; + margin-right: 1rem; + } + #hubzone-map-guide h1 { + margin-top: 0; + } + .usa-layout-docs { + flex-direction: column; + } + .usa-layout-docs-sidenav { + order: 1; + } + .help-nav { + position: relative; + } + .map-guide-header { + border-bottom: none; + padding-bottom: 0.5rem; + } + .return-to-map { + margin-left: 1rem; + } +} diff --git a/app/assets/stylesheets/map.scss b/app/assets/stylesheets/map.scss index 94d49139..eab525d1 100644 --- a/app/assets/stylesheets/map.scss +++ b/app/assets/stylesheets/map.scss @@ -1,6 +1,7 @@ // Globals & Variables @import 'variables'; @import 'globals'; +@import 'layers'; // Partials @import 'map_header'; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1157e883..68820058 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,6 +5,12 @@ class ApplicationController < ActionController::Base before_action :set_locale def set_locale - I18n.locale = params[:locale] || I18n.default_locale + return I18n.locale = I18n.default_locale if params[:locale].nil? + locales = I18n.available_locales + I18n.locale = if locales.include? params[:locale].to_sym + params[:locale] + else + I18n.default_locale + end end end diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb new file mode 100644 index 00000000..b208cb20 --- /dev/null +++ b/app/controllers/help_controller.rb @@ -0,0 +1,6 @@ +# Provides access to the help page with the HUBZone map +class HelpController < ApplicationController + def index + @page_selected = params[:page] + end +end diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb index be7118bf..a500ac62 100644 --- a/app/controllers/map_controller.rb +++ b/app/controllers/map_controller.rb @@ -9,8 +9,7 @@ def index def search query = format_query params path = "#{MAP_CONFIG[:hubzone_api_search_path]}?#{query}" - response = connection.request(method: :get, - path: path) + response = api_get path @body = response.data[:body] respond_to do |format| format.js {} @@ -36,7 +35,14 @@ def parse_search_query(params) private + def api_get(path) + version = MAP_CONFIG[:hubzone_api_version] + connection.request(method: :get, + path: path, + headers: { 'Accept' => "application/sba.hubzone-api.v#{version}" }) + end + def connection - Excon.new(MAP_CONFIG[:hubzone_api_host]) + @connection ||= Excon.new(MAP_CONFIG[:hubzone_api_host]) end end diff --git a/app/views/map/_map_header.html.erb b/app/views/application/_header.html.erb similarity index 86% rename from app/views/map/_map_header.html.erb rename to app/views/application/_header.html.erb index 101cde72..ed94f7fd 100644 --- a/app/views/map/_map_header.html.erb +++ b/app/views/application/_header.html.erb @@ -10,7 +10,7 @@
    - + + - - - - - - - <%= render partial: "qualification", collection: hubzones %> - - + -