From c5d779c0e62264091a6d4417e5b2e843dd22c3fb Mon Sep 17 00:00:00 2001 From: Matt Forrest Date: Mon, 6 Jan 2020 21:17:07 -0500 Subject: [PATCH] everything --- .DS_Store | Bin 0 -> 6148 bytes public/index.html | 10 +- src/.DS_Store | Bin 0 -> 6148 bytes src/components/CARTOMap.js | 15 +- src/components/CARTOMapPredict.js | 142 ++++ src/components/CARTOVLMap.js | 5 + src/components/Page.js | 9 +- src/components/Predict.js | 46 ++ src/components/layout/BottomBar.js | 446 +++++++++++- src/components/layout/BottomBarPredict.js | 826 ++++++++++++++++++++++ src/components/layout/Header.js | 7 +- src/components/layout/Panel.js | 8 +- src/components/layout/RightBar.js | 175 ++++- src/components/widgets/Category.js | 2 +- src/components/widgets/Histogram.js | 6 +- src/components/widgets/LayerToggle.js | 12 +- src/data/C.js | 8 +- src/data/layers/consumer.js | 171 +++++ src/data/layers/demos.js | 34 + src/data/layers/index.js | 18 +- src/data/layers/mastercard.js | 25 + src/data/layers/poi.js | 27 + src/data/layers/railaccidents.js | 25 - src/data/layers/target.js | 27 + src/data/layers/targetpoints.js | 33 + src/data/layers/usstates.js | 25 - src/routers/AppRouter.js | 3 +- src/styles/base/_base.scss | 2 + src/styles/base/_theme.scss | 92 ++- 29 files changed, 2093 insertions(+), 106 deletions(-) create mode 100644 .DS_Store create mode 100644 src/.DS_Store create mode 100644 src/components/CARTOMapPredict.js create mode 100644 src/components/Predict.js create mode 100644 src/components/layout/BottomBarPredict.js create mode 100644 src/data/layers/consumer.js create mode 100644 src/data/layers/demos.js create mode 100644 src/data/layers/mastercard.js create mode 100644 src/data/layers/poi.js delete mode 100644 src/data/layers/railaccidents.js create mode 100644 src/data/layers/target.js create mode 100644 src/data/layers/targetpoints.js delete mode 100644 src/data/layers/usstates.js diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..aa48b75c8e77a74b877ffff8eb87ea8d278503f6 GIT binary patch literal 6148 zcmeHK%}T>S5Z-O8rihq>V2`!GEsJqaP!gEt|f2bDH4#Rg(hlGLCvlGo5T@(Fw$ zXLdJYu?J5gIs?1k%+Abi=7a1HV~kr1ugjRu7}KC3a#U6b4zG08R4^jPIg+^>L@rpb z?p*nPJK(q1S;As=?Pj09Kk$>Z(|O~ydSh*ULu-O;HLvVhl-a41E)vU$FVH%1(wVFD z(l6`LqA(!WV=tY?oEkZ95^VrfM78>iVJns(%ZKL1s z6>TFQ4m+Ut_KTva?e29CjwiSG5BX#9^ep-1K);Y}g%!MjqRP9o<1mQA8yKmgt0+QZ zfEXYKh=G5_fI0@W&3{!j6-x{d1K(i)_Xh(SqN%WyD7Fsh@cNAY1|kaR_?AF46q*W4 ziQoa@G8Is!a`nXEG9AVZ6=y0eCCYTh#mX>`TDf|>aIrd!8zP)>QzG@m05P!4KwX+T zp8rSi%dC9lFQ<@23=jizQfKRz^6;!RpiQ8mU|fj`2mB^sj!rYv*5T$2c(ODAcQ(%;0G9Z2P?Br-2eap literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html index cd21513..82ef3b9 100644 --- a/public/index.html +++ b/public/index.html @@ -8,16 +8,14 @@ - + - - + - +
- - + \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dabbe0b8e8f2c1672bc32f96f088cb9c97df476c GIT binary patch literal 6148 zcmeHKPfNov6i>G4HbmS(aK~P}?Xa=UorF^7!JAOggUZ&@;tI8qwX>hY+MbBiPMl;G zTh8vi*%kqriIu)7C&Ua#%#cMea+w-1lmQ~vy-xaIH}O4-pkhgUGZEc4!; zMxlsqATqC-XA=?w!~ij{<_zd#ueP=3>!jro1H{1Z8NlV!VQg z0ye%S5Dkr{#zG-PK)5OeRHa-$F}NxRzoGL?jfFy0&bXWz#?do#{X*e#cJLcAopDnk zwZs51u*^VNbsKp9AOHORUoN5^F+dEg6$897w1*ZfN#Ct2i^IFt03Cy(U|ykcQ38g% hiXj)T;wGpR@Ed3Vni>m*-~pi@0Z9Wj#K502@B!$ZQ#Swr literal 0 HcmV?d00001 diff --git a/src/components/CARTOMap.js b/src/components/CARTOMap.js index 418f739..c4b0092 100644 --- a/src/components/CARTOMap.js +++ b/src/components/CARTOMap.js @@ -78,8 +78,17 @@ class CARTOMap extends Component { layer.on('featureClicked', this.openPopup.bind(this)); } + if(options.featureClickColumns && layerName === 'stores') { + console.log(layerName) + layer.on('featureClicked', this.openPopupStores.bind(this)); + } + this.props.client.getLeafletLayer().addTo(this.props.map); + if (other.visible === false) { + layer.hide() + } + return { ...all, [layerName]: { source, style, layer, ...other } }; }, {}); @@ -105,8 +114,8 @@ class CARTOMap extends Component { if (!this.popup.isOpen()) { this.popup.openOn(this.props.map); render(, this.popup._contentNode); - } - } + } + } render() { const { layers } = this.props; @@ -134,4 +143,4 @@ const mapDispatchToProps = dispatch => ({ changeCartoBBox: boundingbox => dispatch(changeCartoBBox(boundingbox)) }); -export default connect(mapStateToProps, mapDispatchToProps)(CARTOMap); +export default connect(mapStateToProps, mapDispatchToProps)(CARTOMap); \ No newline at end of file diff --git a/src/components/CARTOMapPredict.js b/src/components/CARTOMapPredict.js new file mode 100644 index 0000000..c02c641 --- /dev/null +++ b/src/components/CARTOMapPredict.js @@ -0,0 +1,142 @@ +import React, { Component } from 'react'; +import { render } from 'react-dom'; +import L from 'leaflet'; +import carto, { filter, source, style, layer } from '@carto/carto.js'; +import { connect } from 'react-redux'; +import styled from 'styled-components'; +import { storeLayers, setMap, setBboxFilter, changeViewport, changeCartoBBox } from '../actions/actions'; +// import { Widgets, Legend, AirbnbPopup, MobileTabs } from '../components/components'; +import InfoWindow from '../components/InfoWindow' +import layers from '../data/layers'; +import C from '../data/C' +import '@carto/airship-style'; + +// import './index.css'; + +const { BASEMAP, BASEMAP_LABELS, CENTER, ZOOM } = C; + +class CARTOMapPredict extends Component { + + constructor(props) { + super(props); + this.state = { + ...props + } + } + + componentDidMount() { + + // look into adding the map up above + + const map = L.map('map', { zoomControl: false, maxZoom: 18 }).setView(this.props.viewport.center, this.props.viewport.zoom); + + this.props.setMap(map); + + L.tileLayer(BASEMAP).addTo(map); + + // L.control.zoom({ position: 'bottomleft' }).addTo(map); + + this.popup = L.popup({ closeButton: false }); + + // this.setBbbox(map.getBounds()); + + const bboxFilter = new carto.filter.BoundingBoxLeaflet(map); + this.props.changeCartoBBox(bboxFilter); + + map.on('moveend', event => { + const boundingBox = event.target.getBounds(); + const newCenter = event.target.getCenter(); + const newZoom = event.target.getZoom(); + const newViewport = { + center: newCenter, + zoom: newZoom + } + + this.props.setBboxFilter([ + boundingBox._northEast.lat, + boundingBox._northEast.lng, + boundingBox._southWest.lat, + boundingBox._southWest.lng + ]) + + this.props.changeViewport(newViewport); + }); + + + + } + + setupLayers() { + const cartoLayers = Object.keys(layers).reduce((all, layerName) => { + const { options, ...other} = layers[layerName]; + + const source = new carto.source.SQL(other.query); + const style = new carto.style.CartoCSS(other.cartocss); + const layer = new carto.layer.Layer(source, style, options); + + if(options.featureClickColumns) { + layer.on('featureClicked', this.openPopup.bind(this)); + } + + this.props.client.getLeafletLayer().addTo(this.props.map); + + if (other.visible === false) { + layer.hide() + } + + + return { ...all, [layerName]: { source, style, layer, ...other } }; + }, {}); + + // Add all layers at the same time so it doesn't reload multiple times + this.props.client.addLayers(Object.values(cartoLayers).map(item => item.layer)); + + // Labels need to be added after the layers + L.tileLayer(BASEMAP_LABELS).addTo(this.props.map); + console.log(cartoLayers) + this.props.storeLayers(cartoLayers) + } + + componentDidUpdate(prevProps) { + if (!prevProps.map && this.props.map) { + this.setupLayers(); + } + } + + openPopup(featureEvent) { + this.popup.setContent(''); + this.popup.setLatLng(featureEvent.latLng); + + if (!this.popup.isOpen()) { + this.popup.openOn(this.props.map); + render(, this.popup._contentNode); + } + } + + render() { + const { layers } = this.props; + const hasLayers = Object.keys(layers).length > 0; + + return ( +
+ ); + } +} + +const mapStateToProps = state => ({ + client: state.client, + map: state.map, + layers: state.layers, + viewport: state.viewport, + boundingbox: state.boundingbox +}); + +const mapDispatchToProps = dispatch => ({ + storeLayers: layers => dispatch(storeLayers(layers)), + setMap: map => dispatch(setMap(map)), + setBboxFilter: bbox => dispatch(setBboxFilter(bbox)), + changeViewport: viewport => dispatch(changeViewport(viewport)), + changeCartoBBox: boundingbox => dispatch(changeCartoBBox(boundingbox)) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(CARTOMapPredict); diff --git a/src/components/CARTOVLMap.js b/src/components/CARTOVLMap.js index ff3605c..ee39ff4 100644 --- a/src/components/CARTOVLMap.js +++ b/src/components/CARTOVLMap.js @@ -99,6 +99,11 @@ class CARTOVLMap extends Component { // this.props.client.getLeafletLayer().addTo(this.props.map); + if (other.visible === false) { + layer.hide() + } + + return { ...all, [layerName]: { source, viz, layer, ...other } }; }, {}); diff --git a/src/components/Page.js b/src/components/Page.js index 32a6a91..54a5f5b 100644 --- a/src/components/Page.js +++ b/src/components/Page.js @@ -8,6 +8,7 @@ import BottomBar from './layout/BottomBar' import Panel from './layout/Panel' import '@carto/airship-style'; + const Page = () => ( @@ -15,7 +16,7 @@ const Page = () => (
- + {/* ( name='Controls' /> */}
- {/* */} + />
( ); -export default Page; +export default Page; \ No newline at end of file diff --git a/src/components/Predict.js b/src/components/Predict.js new file mode 100644 index 0000000..132476a --- /dev/null +++ b/src/components/Predict.js @@ -0,0 +1,46 @@ +import React from 'react'; +import CARTOMapPredict from './CARTOMapPredict'; +import CARTOVLMap from './CARTOVLMap'; +import Header from './layout/Header' +import RightBar from './layout/RightBar' +import LeftBar from './layout/LeftBar' +import BottomBarPredict from './layout/BottomBarPredict' +import Panel from './layout/Panel' +import '@carto/airship-style'; + +const Page = () => ( + + +
+
+
+
+ + {/* */} +
+ +
+ + {/* */} +
+ + +); + +export default Page; diff --git a/src/components/layout/BottomBar.js b/src/components/layout/BottomBar.js index a92c301..a0bb37e 100644 --- a/src/components/layout/BottomBar.js +++ b/src/components/layout/BottomBar.js @@ -5,36 +5,454 @@ import { setNeighbourhoods } from '../../actions/actions'; import carto, { filter, source, style, layer } from '@carto/carto.js'; import '@carto/airship-style'; import Formula from '../widgets/Formula' +import Histogram from '../widgets/Histogram' +import Category from '../widgets/Category' + +import C from '../../data/C' +import axios from 'axios'; + +const { SQL_API_URL, API_KEY, USERNAME } = C; + +const SQL_CLIENT = axios.create({ + method: 'get', + url: SQL_API_URL, + params: { + api_key: API_KEY + } +}); + class BottomBar extends Component { constructor(props) { super(props); this.state = { - ...props + ...props, + tab: 1, + data: [], + income: [], + edu: [], + demos: [], + marital: [] + } + this.layerEvents = this.layerEvents.bind(this); + + } + + getData() { + let bounds + let bbox + + if (this.props.map) { + bounds = this.props.map.getBounds() + + bbox = `WHERE the_geom @ + ST_MakeEnvelope( + ${bounds._northEast.lng}, + ${bounds._northEast.lat}, + ${bounds._southWest.lng}, + ${bounds._southWest.lat}, + 4326 + )` + } + + const query = ` + select + unnest(array[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85]) as start, + unnest(array[5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90]) as end, + unnest(array[sum(agecy0004), sum(agecy0509),SUM(AGECY1014), + SUM(AGECY1519), + SUM(AGECY2024), + SUM(AGECY2529), + SUM(AGECY3034), + SUM(AGECY3539), + SUM(AGECY4044), + SUM(AGECY4549), + SUM(AGECY5054), + SUM(AGECY5559), + SUM(AGECY6064), + SUM(AGECY6569), + SUM(AGECY7074), + SUM(AGECY7579), + SUM(AGECY8084), + SUM(AGECYGT85)]) as value + FROM ags_sociodemographics + ${bbox} + ` + + SQL_CLIENT.request({ + params: { + q: query + } + }) + .then((result) => { + this.setState({data: result.data.rows}) + }) + .catch((error) => { + console.log(error) + }); + } + + getIncome() { + let bounds + let bbox + + if (this.props.map) { + bounds = this.props.map.getBounds() + + bbox = `WHERE the_geom @ + ST_MakeEnvelope( + ${bounds._northEast.lng}, + ${bounds._northEast.lat}, + ${bounds._southWest.lng}, + ${bounds._southWest.lat}, + 4326 + )` + } + + const query = ` select unnest(array[0, 10, 15, 20, 25, 30, 35, 34, 45, 50, 60, 75, 100, 125, 150, 200]) as start, unnest(array[10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 75, 100, 125, 150, 200, 300]) as end, unnest(array[SUM(HINCYLT10), SUM(HINCY1015), SUM(HINCY1520), SUM(HINCY2025), SUM(HINCY2530), SUM(HINCY3035), SUM(HINCY3540), SUM(HINCY4045), SUM(HINCY4550), SUM(HINCY5060), SUM(HINCY6075), SUM(HINCY75100), SUM(HINCY10025), SUM(HINCY12550), SUM(HINCY15020), SUM(HINCYGT200)]) as value FROM ags_sociodemographics ${bbox} + ` + + SQL_CLIENT.request({ + params: { + q: query + } + }) + .then((result) => { + this.setState({income: result.data.rows}) + }) + .catch((error) => { + console.log(error) + }); + } + + getEducation() { + let bounds + let bbox + + if (this.props.map) { + bounds = this.props.map.getBounds() + + bbox = `WHERE the_geom @ + ST_MakeEnvelope( + ${bounds._northEast.lng}, + ${bounds._northEast.lat}, + ${bounds._southWest.lng}, + ${bounds._southWest.lat}, + 4326 + )` + } + + const query = ` select unnest(array['Less Than 9th Grade', 'High School No Diploma', 'High School Graduate', 'College No Diploma', 'Associates Degree', 'Bachelors Degree', 'Graduate or Higher Degree']) as name, unnest(array[SUM(EDUCYLTGR9), SUM(EDUCYSHSCH), SUM(EDUCYHSCH), SUM(EDUCYSCOLL), SUM(EDUCYASSOC), SUM(EDUCYBACH), SUM(EDUCYGRAD)]) as value FROM ags_sociodemographics ${bbox} order by value desc LIMIT 5 ` + + SQL_CLIENT.request({ + params: { + q: query } + }) + .then((result) => { + this.setState({edu: result.data.rows}) + }) + .catch((error) => { + console.log(error) + }); + } + + getDemos() { + let bounds + let bbox + + if (this.props.map) { + bounds = this.props.map.getBounds() + + bbox = `WHERE the_geom @ ST_MakeEnvelope( ${bounds._northEast.lng}, ${bounds._northEast.lat}, ${bounds._southWest.lng}, ${bounds._southWest.lat}, 4326 )` + } + + const query = ` select unnest(array['Non Hispanic White', 'Non Hispanic Black', 'Non Hispanic American Indian', 'Non Hispanic Asian', 'Non Hispanic Hawaiian/Pacific Islander', 'Non Hispanic Other Race', 'Non Hispanic Multiple Race', 'Population Hispanic']) as name, unnest(array[SUM(RCHCYWHNHS), SUM(RCHCYBLNHS), SUM(RCHCYAMNHS), SUM(RCHCYASNHS), SUM(RCHCYHANHS), SUM(RCHCYOTNHS), SUM(RCHCYMUNHS), SUM(HISCYHISP)]) as value FROM ags_sociodemographics ${bbox} order by value desc LIMIT 5 ` + + SQL_CLIENT.request({ + params: { + q: query + } + }) + .then((result) => { + this.setState({demos: result.data.rows}) + }) + .catch((error) => { + console.log(error) + }); + } + + getMarrital() { + let bounds + let bbox + + if (this.props.map) { + bounds = this.props.map.getBounds() + + bbox = `WHERE the_geom @ + ST_MakeEnvelope( + ${bounds._northEast.lng}, + ${bounds._northEast.lat}, + ${bounds._southWest.lng}, + ${bounds._southWest.lat}, + 4326 + )` + } + + const query = ` select unnest(array['Never Married', 'Now Married', 'Separated', 'Widowed', 'Divorced']) as name, unnest(array[ SUM(MARCYNEVER), SUM(MARCYMARR), SUM(MARCYSEP), SUM(MARCYWIDOW), SUM(MARCYDIVOR)]) as value FROM ags_sociodemographics ${bbox} order by value desc LIMIT 5 ` + + SQL_CLIENT.request({ + params: { + q: query + } + }) + .then((result) => { + this.setState({marital: result.data.rows}) + }) + .catch((error) => { + console.log(error) + }); + } + + + + componentDidMount() { + // this.tab.activeTab = this.state.tab + const { income, data, edu, demos, marital } = this.state + this.pyramid.data = data; + this.income.data = income; + this.edu.categories = edu; + this.demos.categories = demos + this.marital.categories = marital + this.edu.visibleCategories = "5" + } + + layerEvents() { + if (this.tabs.activeTab <= 2) { + this.props.layers.consumer.layer.hide() + this.props.layers.demos.layer.show() + this.props.layers.mastercard.layer.hide() + this.props.layers.poi.layer.hide() + } + if (this.tabs.activeTab === 3) { + this.props.layers.demos.layer.hide() + this.props.layers.consumer.layer.show() + this.props.layers.mastercard.layer.hide() + this.props.layers.poi.layer.hide() + } + if (this.tabs.activeTab === 4) { + this.props.layers.demos.layer.hide() + this.props.layers.consumer.layer.hide() + this.props.layers.mastercard.layer.show() + this.props.layers.poi.layer.hide() + } + if (this.tabs.activeTab === 5) { + this.props.layers.demos.layer.hide() + this.props.layers.consumer.layer.hide() + this.props.layers.mastercard.layer.hide() + this.props.layers.poi.layer.show() + } + } + + componentDidUpdate(prevProps) { + console.log(prevProps) + if (this.tabs.activeTab === 3) { + this.props.layers.demos.layer.hide() + } + if (this.tabs.activeTab === 1) { + this.props.layers.consumer.layer.hide() + } + + if (prevProps != this.props) { + if (this.tabs.activeTab === 3) { + this.props.layers.demos.layer.hide() + } + if (this.tabs.activeTab === 1) { + this.props.layers.consumer.layer.hide() + } + this.getData() + this.getIncome() + this.getEducation() + this.getDemos() + this.getMarrital() + this.setState({ tab: this.tabs.activeTab }) + + console.log(this.state.tab) + } + if (prevProps.data != this.state.data) { + this.pyramid.data = this.state.data + this.income.data = this.state.income + this.edu.categories = this.state.edu + this.demos.categories = this.state.demos + this.marital.categories = this.state.marital + + this.edu.visibleCategories = "5" + } } render() { + + + + const background = `as-map-footer ${this.props.background}` return (
-
- -
+ { this.tabs = node; }} onClick={this.layerEvents}> + +
+
+ +
+ { this.pyramid = node; }} + heading="Population Pyramid" + description="Population pyramid for areas in view (buckets segmented by ranges of five years)" + show-header + disable-interactivity + data={this.state.data} + show-clear> + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ { this.income = node; }} + heading="Income Pyramid" + description="Household income pyramid for areas in view" + show-header + disable-interactivity + data={this.state.income} + show-clear> + +
+ +
+ +
+
+ +
+ { this.edu = node; }} + heading='Education' + description='Educational attainment of population in view' + categories={this.state.edu} + disable-interactivity + /> +
+
+ { this.demos = node; }} + heading='Demographics' + description='Demographics of population in view' + categories={this.state.demos} + disable-interactivity + /> +
+
+ { this.marital = node; }} + heading='Marital Status' + description='Marital status of population in view' + categories={this.state.marital} + disable-interactivity + /> +
+ {/*

households formula

+

ethnicity cat

*/} + +
+
+ +

Table

+
+
+

Score Formulas and Histograms

+
+
+

Category

+

Total POI formula

+
+
) diff --git a/src/components/layout/BottomBarPredict.js b/src/components/layout/BottomBarPredict.js new file mode 100644 index 0000000..2a58324 --- /dev/null +++ b/src/components/layout/BottomBarPredict.js @@ -0,0 +1,826 @@ +import React, { Component } from 'react'; +import { render } from 'react-dom'; +import { connect } from 'react-redux'; +import { setNeighbourhoods } from '../../actions/actions'; +import carto, { filter, source, style, layer } from '@carto/carto.js'; +import '@carto/airship-style'; +import Formula from '../widgets/Formula' +import axios from 'axios'; +import C from '../../data/C' + + +const { SQL_API_URL, API_KEY, USERNAME } = C; + + + + +class BottomBarPredict extends Component { + + constructor(props) { + super(props); + this.state = { + ...props, + data: [], + featureList: ['total_pop', + 'pop_sqkm', + 'fem_under_5', + 'fem_5_to_9', + 'fem_10_to_14', + 'fem_15_to_17', + 'fem_18_to_19', + 'fem_20', + 'fem_21', + 'fem_22_to_24', + 'fem_25_to_29', + 'fem_30_to_34', + 'fem_35_to_39', + 'fem_40_to_44', + 'fem_45_to_49', + 'fem_50_to_54', + 'fem_55_to_59', + 'fem_60_to_61', + 'fem_62_to_64', + 'fem_65_to_66', + 'fem_67_to_69', + 'fem_70_to_74', + 'fem_75_to_79', + 'fem_80_to_84', + 'fem_over_85', + 'male_under_5', + 'male_5_to_9', + 'male_10_to_14', + 'male_15_to_17', + 'male_18_to_19', + 'male_20', + 'male_21', + 'male_22_to_24', + 'male_25_to_29', + 'male_30_to_34', + 'male_35_to_39', + 'male_40_to_44', + 'male_45_to_49', + 'male_50_to_54', + 'male_55_to_59', + 'male_60_to_61', + 'male_62_to_64', + 'male_65_to_66', + 'male_67_to_69', + 'male_70_to_74', + 'male_75_to_79', + 'male_80_to_84', + 'male_over_85', + 'med_income', + 'inc_under_10k', + 'inc_10_to_15k', + 'inc_15_to_20k', + 'inc_20_to_25k', + 'inc_25_to_30k', + 'inc_30_to_35k', + 'inc_35_to_40k', + 'inc_40_to_45k', + 'inc_45_to_50k', + 'inc_50_to_60k', + 'inc_60_to_75k', + 'inc_75_to_100k', + 'inc_100_to_125k', + 'inc_125_to_150k', + 'inc_150_to_200k', + 'inc_over_200k', + 'edu_no_high_school', + 'edu_no_high_school_or_ged', + 'edu_some_college', + 'edu_finished_high_school', + 'edu_under_one_year_college', + 'edu_over_1_year_college_no_degree', + 'edu_associates_degree', + 'edu_bachelors_degree', + 'edu_masters', + 'homes_occupied_housing_units', + 'homes_single_family_units', + 'homes_single_family_attached', + 'homes_mobile_homes', + 'homes_built_after_2005', + 'homes_built_between_2000_2004', + 'homes_built_before_1939', + 'homes_median_year_structure_built', + 'homes_owner_occupied_housing_units', + 'homes_households', + 'car_free_houseolds', + 'one_car_households', + 'two_car_households', + 'three_car_households', + 'four_car_households', + 'commuters_by_car', + 'walked_to_work', + 'work_at_home', + 'commute_public_transit', + 'sales_score', + 'sales_metro_score', + 'sales_state_score', + 'total_merchants', + 'transactions_score', + 'transactions_metro_score', + 'transactions_state_score', + 'ticket_size_score', + 'ticket_size_metro_score', + 'ticket_size_state_score', + 'growth_score', + 'growth_metro_score', + 'growth_state_score', + 'stability_score', + 'stability_metro_score', + 'stability_state_score', + 'retail_home_goods', + 'retail_groceries', + 'retail_special_foods', + 'retail_pharmacy', + 'retail_beauthy', + 'retail_health', + 'retial_mens', + 'retail_womens', + 'retail_kids', + 'retail_family', + 'retail_clothing', + 'retail_sport', + 'retail_toys', + 'retail_department_stores', + 'send_all_2019', + 'spend_mens_clothes', + 'spend_boys_clothes', + 'spend_womesn_clothes', + 'spend_girls_clothes', + 'spend_infants', + 'spend_shoes', + 'spend_apparel', + 'spend_books', + 'spend_electronics', + 'spend_recreation', + 'spend_food', + 'spend_applicances', + 'spend_toiletries_', + 'spend_all_2024', + 'mobility_black', + 'mobility_native_american', + 'mobility_asian_', + 'mobility_female', + 'mobility_hispanic', + 'mobility_male', + 'mobility_white', + 'mobility_avg_age', + 'mobility_median', + 'mobility_0_to_5_min_home', + 'mobility_10_to_15_min_home', + 'mobility_5_to_10_min_home', + 'mobility_avg_dist_home', + 'mobility_0_to_5_min_work', + 'mobility_10_to_15_min_work', + 'mobility_5_to_10_min_work', + 'mobility_avg_dist_work', + 'mobilty_stay_time', + 'mobility_visits_sun', + 'mobility_visits_mon', + 'mobility_visits_tue', + 'mobility_visits_wed', + 'mobility_visits_thu', + 'mobility_visits_fri', + 'mobility_visits_sat', + 'best_buy', + 'costco', + 'home_depot', + 'k_mart', + 'kohls', + 'kroger', + 'walmart', + 'all_competitors', + 'poi_bakery', + 'poi_cinema', + 'poi_toys_sporting_goods', + 'poi_appliances', + 'poi_electronics', + 'poi_furniture', + 'poi_wholesale_clubs', + 'poi_cloting', + 'total_poi'] + } + + this.callAPI = this.callAPI.bind(this) + this.callCheckAPI = this.callCheckAPI.bind(this) + } + + + + + componentDidMount() { + this.ageDemos() + this.incomeDemos() + this.incomeDemos() + this.educationDemos() + this.housingDemos() + this.transportationDemos() + this.mastercardSales() + this.mastercardTransactions() + this.mastercardTicket() + this.mastercardGrowth() + this.mastercardStability() + this.retailPotential() + this.consumerSpend() + this.mobilityDemos() + this.mobilityTravel() + this.mobilityDays() + this.competitorLocations() + this.pois() + + this.categoryWidget.showHeader = true; + this.categoryWidget.showClearButton = false; + this.categoryWidget.useTotalPercentage = false; + this.categoryWidget.visibleCategories = 30; + this.categoryWidget.categories = this.state.data + this.categoryWidget.disableInteractivity = true + + console.log(this.state.data) + + // this.storeFeatures() + } + + componentDidUpdate(prevProps) { + if (prevProps.data != this.state.data) { + this.categoryWidget.categories = this.state.data + } + } + + callCheckAPI(task) { + const url = `http://0.0.0.0:8885/check/${task}?external=True` + setTimeout(() => { + axios.get(url) + .then((result) => { + console.log(result) + return result; + }) + .then((jobStatus) => { + console.log('STATUS: ', jobStatus) + if (jobStatus.data === 'PENDING') { + this.callCheckAPI(task); + console.log('CHECKING --> ', task) + } else if (jobStatus.data['Feature Importance']) { + console.log('Success!: ', jobStatus) + const d = jobStatus.data["Feature Importance"] + + const formatted = Object.entries(d).map(([k, v]) => ({'name': k, 'value': v})); + + const sort = formatted.sort((a, b) => b.value-a.value) + + console.log(sort) + + this.setState({ data: sort }) + + } + }).catch((error) => { + console.log(error) + }) + + }, 5000) +} + + callAPI() { + const API_TARGET = axios.create({ + method: 'get', + url: 'http://0.0.0.0:8885/revenue?', + params: { + api_key: API_KEY, + username: USERNAME, + origin_table: 'target_predict_new', + target_table: 'target_predict_new', + prediction_table: 'target_predict__new_result', + id_colname: 'id', + lnglat_colname: 'lng,lat', + target_colname: 'revenue', + test_size: 0.3, + random_seed: 112358, + feature_colname: this.state.featureList.join(',') + } + }); + + API_TARGET.request( + // { + // params: { + // q: finalQuery + // } + // } + ) + .then((result) => { + console.log(result) + this.callCheckAPI(result.data.task_id) + }) + .catch((error) => { + console.log(error) + }) + + } + + ageDemos() { + const values = ['med_age', 'total_pop', 'pop_sqkm', 'fem_under_5', 'fem_5_to_9', 'fem_10_to_14', 'fem_15_to_17', 'fem_18_to_19', 'fem_20', 'fem_21', 'fem_22_to_24', 'fem_25_to_29', 'fem_30_to_34', 'fem_35_to_39', 'fem_40_to_44', 'fem_45_to_49', 'fem_50_to_54', 'fem_55_to_59', 'fem_60_to_61', 'fem_62_to_64', 'fem_65_to_66', 'fem_67_to_69', 'fem_70_to_74', 'fem_75_to_79', 'fem_80_to_84', 'fem_over_85', 'male_under_5', 'male_5_to_9', 'male_10_to_14', 'male_15_to_17', 'male_18_to_19', 'male_20', 'male_21', 'male_22_to_24', 'male_25_to_29', 'male_30_to_34', 'male_35_to_39', 'male_40_to_44', 'male_45_to_49', 'male_50_to_54', 'male_55_to_59', 'male_60_to_61', 'male_62_to_64', 'male_65_to_66', 'male_67_to_69', 'male_70_to_74', 'male_75_to_79', 'male_80_to_84', 'med_age', 'total_pop', 'pop_sqkm', 'fem_under_5', 'fem_5_to_9', 'fem_10_to_14', 'fem_15_to_17', 'fem_18_to_19', 'fem_20', 'fem_21', 'fem_22_to_24', 'fem_25_to_29', 'fem_30_to_34', 'fem_35_to_39', 'fem_40_to_44', 'fem_45_to_49', 'fem_50_to_54', 'fem_55_to_59', 'fem_60_to_61', 'fem_62_to_64', 'fem_65_to_66', 'fem_67_to_69', 'fem_70_to_74', 'fem_75_to_79', 'fem_80_to_84', 'fem_over_85', 'male_under_5', 'male_5_to_9', 'male_10_to_14', 'male_15_to_17', 'male_18_to_19', 'male_20', 'male_21', 'male_22_to_24', 'male_25_to_29', 'male_30_to_34', 'male_35_to_39', 'male_40_to_44', 'male_45_to_49', 'male_50_to_54', 'male_55_to_59', 'male_60_to_61', 'male_62_to_64', 'male_65_to_66', 'male_67_to_69', 'male_70_to_74', 'male_75_to_79', 'male_80_to_84', 'male_over_85'] + + this.age_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) + } + + incomeDemos() { + const values = ['male_over_85', + 'med_income', + 'inc_under_10k', + 'inc_10_to_15k', + 'inc_15_to_20k', + 'inc_20_to_25k', + 'inc_25_to_30k', + 'inc_30_to_35k', + 'inc_35_to_40k', + 'inc_40_to_45k', + 'inc_45_to_50k', + 'inc_50_to_60k', + 'inc_60_to_75k', + 'inc_75_to_100k', + 'inc_100_to_125k', + 'inc_125_to_150k', + 'inc_150_to_200k', + 'inc_over_200k'] + + this.income_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +educationDemos() { + const values = ['edu_no_high_school', + 'edu_no_high_school_or_ged', + 'edu_some_college', + 'edu_finished_high_school', + 'edu_under_one_year_college', + 'edu_over_1_year_college_no_degree', + 'edu_associates_degree', + 'edu_bachelors_degree', + 'edu_masters'] + + this.education_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +housingDemos() { + const values = ['homes_occupied_housing_units', + 'homes_single_family_units', + 'homes_single_family_attached', + 'homes_mobile_homes', + 'homes_built_after_2005', + 'homes_built_between_2000_2004', + 'homes_built_before_1939', + 'homes_median_year_structure_built', + 'homes_owner_occupied_housing_units', + 'homes_households'] + + this.housing_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +transportationDemos() { + const values = ['car_free_houseolds', + 'one_car_households', + 'two_car_households', + 'three_car_households', + 'four_car_households', + 'commuters_by_car', + 'walked_to_work', + 'work_at_home', + 'commute_public_transit'] + + this.transportation_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mastercardSales() { + const values = ['sales_score', + 'sales_metro_score', + 'sales_state_score', + 'total_merchants'] + + this.sales_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mastercardTransactions() { + const values = ['transactions_score', + 'transactions_metro_score', + 'transactions_state_score'] + + this.transactions_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mastercardTicket() { + const values = ['ticket_size_score', + 'ticket_size_metro_score', + 'ticket_size_state_score'] + + this.ticket_size_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mastercardGrowth() { + const values = ['growth_score', + 'growth_metro_score', + 'growth_state_score'] + + this.growth_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mastercardStability() { + const values = ['stability_score', + 'stability_metro_score', + 'stability_state_score'] + + this.stability_widget.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + + +consumerSpend() { + const values = ['send_all_2019', + 'spend_mens_clothes', + 'spend_boys_clothes', + 'spend_womesn_clothes', + 'spend_girls_clothes', + 'spend_infants', + 'spend_shoes', + 'spend_apparel', + 'spend_books', + 'spend_electronics', + 'spend_recreation', + 'spend_food', + 'spend_applicances', + 'spend_toiletries_', + 'spend_all_2024'] + + this.consumer_spend.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +retailPotential() { + const values = ['retail_home_goods', + 'retail_groceries', + 'retail_special_foods', + 'retail_pharmacy', + 'retail_beauthy', + 'retail_health', + 'retial_mens', + 'retail_womens', + 'retail_kids', + 'retail_family', + 'retail_clothing', + 'retail_sport', + 'retail_toys', + 'retail_department_stores'] + + this.retail_potential.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mobilityDemos() { + const values = ['mobility_black', + 'mobility_native_american', + 'mobility_asian_', + 'mobility_female', + 'mobility_hispanic', + 'mobility_male', + 'mobility_white', + 'mobility_avg_age', + 'mobility_median'] + + this.mobility_demos.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + + + +mobilityTravel() { + const values = ['mobility_0_to_5_min_home', + 'mobility_10_to_15_min_home', + 'mobility_5_to_10_min_home', + 'mobility_avg_dist_home', + 'mobility_0_to_5_min_work', + 'mobility_0_to_5_min_work', + 'mobility_0_to_5_min_work', + 'mobility_avg_dist_work'] + + this.mobility_travel.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +mobilityDays() { + const values = ['mobilty_stay_time', + 'mobility_visits_sun', + 'mobility_visits_mon', + 'mobility_visits_tue', + 'mobility_visits_wed', + 'mobility_visits_thu', + 'mobility_visits_fri', + 'mobility_visits_sat',] + + this.mobility_weekday.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +competitorLocations() { + const values = ['best_buy', + 'costco', + 'home_depot', + 'k_mart', + 'kohls', + 'kroger', + 'walmart', + 'all_competitors'] + + this.competitors.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + +pois() { + const values = ['poi_bakery', + 'poi_cinema', + 'poi_toys_sporting_goods', + 'poi_appliances', + 'poi_electronics', + 'poi_furniture', + 'poi_wholesale_clubs', + 'poi_cloting', + 'total_poi'] + + this.poi.addEventListener('change', (event) => { + console.log(event) + if (event.detail === true) { + const pushedArray = this.state.featureList.concat(values) + this.setState({ featureList: pushedArray }) + } else if (event.detail === false) { + const udpatedData = this.state.featureList.filter((item) => { + return !values.includes(item) + }) + this.setState({ featureList: udpatedData }) + } + console.log(this.state.featureList) + }) +} + + render() { + + const background = `as-map-footer ${this.props.background}` + const { data } = this.state; + + + return ( +
+
+ {/* API Form */} + +
+
+ { this.categoryWidget = node; }} + heading='Feature Importance' + description='Features in the model ranked by relative importance in the prediction' + categories={data} + > + +
+ + +
+
+ +
+

{ this.age_widget = node; }} label="Age">

+

{ this.income_widget = node; }} label="Income">

+

{ this.education_widget = node; }} label="Education">

+

{ this.housing_widget = node; }} label="Housing">

+

{ this.transportation_widget = node; }} label="Transportation">

+
+
+

{ this.sales_widget = node; }} label="Spend">

+

{ this.ticket_size_widget = node; }} label="Ticket Size">

+

{ this.transactions_widget = node; }} label="Transactions">

+

{ this.growth_widget = node; }} label="Growth">

+

{ this.stability_widget = node; }} label="Stability">

+
+
+

{ this.consumer_spend = node; }} label="Consumer Spend">

+

{ this.retail_potential = node; }} label="Retail Potential">

+
+
+

{ this.mobility_demos = node; }} label="Sociodemographics">

+

{ this.mobility_weekday = node; }} label="Weekday Breakdown">

+

{ this.mobility_travel = node; }} label="Distance Traveled from Home">

+
+
+

{ this.competitors = node; }} label="Competitors">

+
+
+

{ this.poi = node; }} label="All POIs">

+ {/* Roads */} +
+ {/*
+

{ this.store_features = node; }} label="All Store Features">

+
*/} +
+
+
+ ) + } +} + +const mapStateToProps = state => ({ + client: state.client, + map: state.map, + layers: state.layers, + viewport: state.viewport, + boundingbox: state.boundingbox +}); + +const mapDispatchToProps = dispatch => ({ + setNeighbourhoods: selected => dispatch(setNeighbourhoods(selected)), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(BottomBarPredict); diff --git a/src/components/layout/Header.js b/src/components/layout/Header.js index b8af788..5e05170 100644 --- a/src/components/layout/Header.js +++ b/src/components/layout/Header.js @@ -24,14 +24,15 @@ class Header extends Component {
diff --git a/src/components/layout/Panel.js b/src/components/layout/Panel.js index d78378c..fbc7f83 100644 --- a/src/components/layout/Panel.js +++ b/src/components/layout/Panel.js @@ -43,12 +43,10 @@ class Panel extends Component {
-
diff --git a/src/components/layout/RightBar.js b/src/components/layout/RightBar.js index 984205b..58669ad 100644 --- a/src/components/layout/RightBar.js +++ b/src/components/layout/RightBar.js @@ -21,7 +21,8 @@ class RightBar extends Component { } state = { - size: null + size: null, + data: null }; componentDidMount() { @@ -29,16 +30,180 @@ class RightBar extends Component { this.setState({size: z}) } + render() { + this.props.layers.target.layer.on('featureClicked', e => { + this.setState({ data: e.data }) + }); + + + const { data } = this.state + + let popUp + const wine = ['Wine', 'Wine Available', 'Wine & Beer Available'] + const beer = ['Beer', 'Beer Available', 'Wine & Beer Available'] + + if (!data) { + popUp =

Click a store to see more information

+ } else { + const storeFeatures = data.store_features.replace(/["']/g, "").replace('[', '').replace(']', '').split(', ') + const storeFeaturesArray = Array.from(data.store_features.replace(/["']/g, "").replace('[', '').replace(']', '').split(', ')) + + const beerTrue = beer.some(i => storeFeaturesArray.includes(i)) + const wineTrue = wine.some(i => storeFeaturesArray.includes(i)) + + const timeValues = JSON.parse(data.timings.replace(/'/g, '"')) + + const hours = timeValues.map((timeValues, index) => +
+

{timeValues["Week Day"]}: {timeValues["Open Hours"]}

+
+ ) + + const gmaps = `https://maps.google.com/?q=${data.formatted}` + + popUp =
+

Store No. {data.id}

+

{data.store_name}

+

{data.formatted}

+

{data.city}, {data.state}

+

Phone: {data.contact}

+
+ +
+
+
+
+ {hours} +
+
+
+

Store Information

+

Date Opened: {data.open_formatted}

+ {data.last_remodel_formatted ?

Date Reformatted: {data.last_remodel_formatted}

: '' } +

Store Type: {data.type}

+ {data.subtype ?

Store Subtype: {data.subtype}

: '' } + +
+
+
+ +

Store Features

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAt Store
CVS Pharmacy{storeFeatures.includes('CVS pharmacy') ? : '' }
Minute Clinic{storeFeatures.includes('MinuteClinic') ? : '' }
Delivery From Store{storeFeatures.includes('Delivery From Store') ? : '' }
Starbucks{storeFeatures.includes('Starbucks') ? : '' }
Grocery{storeFeatures.includes('Fresh Grocery') ? : '' }
Expanded Grocery{storeFeatures.includes('Expanded Grocery') ? : '' }
Wine{wineTrue ? : '' }
Beer{beerTrue ? : '' }
Photo{storeFeatures.includes('Photo Lab') ? : '' }
Bakery{storeFeatures.includes('Bakery') ? : '' }
Deli{storeFeatures.includes('Deli') ? : '' }
Optical{storeFeatures.includes('Optical') ? : '' }
Accepts WIC{storeFeatures.includes('Accepts WIC') ? : '' }
+
+
+ } + + return ( diff --git a/src/components/widgets/Category.js b/src/components/widgets/Category.js index 88b1a62..cc6d6f7 100644 --- a/src/components/widgets/Category.js +++ b/src/components/widgets/Category.js @@ -55,7 +55,7 @@ class Category extends Component { } _addDataview() { this.dataView = new carto.dataview.Category(this.props.categoryLayer, this.props.column, { - limit: 10, + limit: this.props.limit, operation: this.props.operation, operationColumn: this.props.operationColumn }); diff --git a/src/components/widgets/Histogram.js b/src/components/widgets/Histogram.js index b3b1c62..2b7445f 100644 --- a/src/components/widgets/Histogram.js +++ b/src/components/widgets/Histogram.js @@ -38,6 +38,10 @@ class Histogram extends Component { setupConfig() { const { data } = this.state this.widget.data = data; + // this.widget.xAxisOptions ={ + // format: (value) => `${value.split(',').join('')}`, + // values: [1990, 2000] + // } } addDataview() { @@ -82,7 +86,7 @@ class Histogram extends Component { onSelectedChanged = ({ detail }) => { let { filter } = this.state; - if (!detail.length) { + if (!detail) { this.props.layer.removeFilter(filter); filter = null; this.setState({ filter }); diff --git a/src/components/widgets/LayerToggle.js b/src/components/widgets/LayerToggle.js index f462f36..b228f3d 100644 --- a/src/components/widgets/LayerToggle.js +++ b/src/components/widgets/LayerToggle.js @@ -19,10 +19,20 @@ class LayerToggle extends Component { setupConfig() { const { checked } = this.state; - this.widget.checked = checked + this.widget.checked = this.props.layer.visible } + // componentDidUpdate() { + // console.log(this.widget.checked) + // if (this.props.layer.visible === true) { + // console.log(this.widget) + // } else if (this.props.layer.visible === false) { + // this.widget.checked = false + // } + // } + setupChecked() { + this.setState({ checked: this.props.layer.visible }) this.widget.addEventListener('change', (event) => { this.setState({ checked: event.detail }) if (event.detail === true) { diff --git a/src/data/C.js b/src/data/C.js index ef905f3..01ea732 100644 --- a/src/data/C.js +++ b/src/data/C.js @@ -1,10 +1,10 @@ const C = { - SQL_API_URL: 'https://mforrest.carto.com/api/v2/sql?q=', - IMPORT_API_URL: 'https://mforrest.carto/api/v1/imports', + SQL_API_URL: 'https://mbforrcdb.carto.com/api/v2/sql?', + IMPORT_API_URL: 'https://mbforrcdb.carto/api/v1/imports', BASEMAP: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png', BASEMAP_LABELS: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}.png', - USERNAME: 'mforrest', - API_KEY: 'default_public', + USERNAME: 'mbforrcdb', + API_KEY: 'b93d8a3b37e53fc20361c5ce5369b2c7b594bd44', CENTER: [33.753707, -84.389363], ZOOM: 6 }; diff --git a/src/data/layers/consumer.js b/src/data/layers/consumer.js new file mode 100644 index 0000000..0ccf9b4 --- /dev/null +++ b/src/data/layers/consumer.js @@ -0,0 +1,171 @@ +export default { + name: 'Consumer Profiles', + + visible: false, + + cartocss: ` + #layer { + polygon-fill: ramp([normalized], (#d2fbd4, #a5dbc2, #7bbcb0, #559c9e, #3a7c89, #235d72, #123f5a), quantiles); + } + #layer::outline { + line-width: 0; + line-color: #FFFFFF; + line-opacity: 0.5; + } + `, + + query: ` + SELECT + cartodb_id, + the_geom, + the_geom_webmercator, + POPCY/st_area(the_geom) as normalized, + POPCY AS Population_2019A, + HHDCY AS Households_2019A, + PMACYHH1PA AS Panorama_1st_Largest_Segment_Households, + PMACYHD01 AS One_Percenters_hh, + PMACYHD02 AS Peak_Performers_hh, + PMACYHD03 AS Second_City_Moguls_hh, + PMACYHD04 AS Sprawl_Success_hh, + PMACYHD05 AS Transitioning_Affluent_Families_hh, + PMACYHD06 AS Best_of_Both_Worlds_hh, + PMACYHD07 AS Upscale_Diversity_hh, + PMACYHD08 AS Living_the_Dream_hh, + PMACYHD09 AS Successful_Urban_Refugees_hh, + PMACYHD10 AS Emerging_Leaders_hh, + PMACYHD11 AS Affluent_Newcomers_hh, + PMACYHD12 AS Mainstream_Established_Suburbs_hh, + PMACYHD13 AS Cowboy_Country_hh, + PMACYHD14 AS American_Playgrounds_hh, + PMACYHD15 AS Comfortable_Retirement_hh, + PMACYHD16 AS Spacious_Suburbs_hh, + PMACYHD17 AS New_American_Dreams_hh, + PMACYHD18 AS Small_Town_Middle_Managers_hh, + PMACYHD19 AS Outer_Suburban_Affluence_hh, + PMACYHD20 AS Rugged_Individualists_hh, + PMACYHD21 AS New_Suburban_Style_hh, + PMACYHD22 AS Up_and_Coming_Suburban_Diversity_hh, + PMACYHD23 AS Enduring_Heartland_hh, + PMACYHD24 AS Isolated_Hispanic_Neighborhoods_hh, + PMACYHD25 AS Hipsters_and_Geeks_hh, + PMACYHD26 AS High_Density_Diversity_hh, + PMACYHD27 AS Young_Coastal_Technocrats_hh, + PMACYHD28 AS Asian_Hispanic_Fusion_hh, + PMACYHD29 AS Big_Apple_Dreamers_hh, + PMACYHD30 AS True_Grit_hh, + PMACYHD31 AS Working_Hispania_hh, + PMACYHD32 AS Struggling_Singles_hh, + PMACYHD33 AS Noreasters_hh, + PMACYHD34 AS Midwestern_Comforts_hh, + PMACYHD35 AS Generational_Dreams_hh, + PMACYHD36 AS Olde_New_England_hh, + PMACYHD37 AS Faded_Industrial_Dreams_hh, + PMACYHD38 AS Failing_Prospects_hh, + PMACYHD39 AS Second_City_Beginnings_hh, + PMACYHD40 AS Beltway_Commuters_hh, + PMACYHD41 AS Garden_Variety_Suburbia_hh, + PMACYHD42 AS Rising_Fortunes_hh, + PMACYHD43 AS Classic_Interstate_Suburbia_hh, + PMACYHD44 AS Pacific_Second_City_hh, + PMACYHD45 AS Northern_Blues_hh, + PMACYHD46 AS Recessive_Singles_hh, + PMACYHD47 AS Simply_Southern_hh, + PMACYHD48 AS Tex_Mex_hh, + PMACYHD49 AS Sierra_Siesta_hh, + PMACYHD50 AS Great_Plains_Great_Struggles_hh, + PMACYHD51 AS Boots_and_Brew_hh, + PMACYHD52 AS Great_Open_Country_hh, + PMACYHD53 AS Classic_Dixie_hh, + PMACYHD54 AS Off_the_Beaten_Path_hh, + PMACYHD55 AS Hollows_and_Hills_hh, + PMACYHD56 AS Gospel_and_Guns_hh, + PMACYHD57 AS Cap_and_Gown_hh, + PMACYHD58 AS Marking_Time_hh, + PMACYHD59 AS Hispanic_Working_Poor_hh, + PMACYHD60 AS Bordertown_Blues_hh, + PMACYHD61 AS Communal_Living_hh, + PMACYHD62 AS Living_Here_in_Allentown_hh, + PMACYHD63 AS Southern_Small_City_Blues_hh, + PMACYHD64 AS Struggling_Southerners_hh, + PMACYHD65 AS Forgotten_Towns_hh, + PMACYHD66 AS Post_Industrial_Trauma_hh, + PMACYHD67 AS Starting_Out_hh, + PMACYHD68 AS Rust_Belt_Poverty_hh, + PMACYPP1PA AS argest_Segment_Population, + PMACYPP01 AS One_Percenters, + PMACYPP02 AS Peak_Performers, + PMACYPP03 AS Second_City_Moguls, + PMACYPP04 AS Sprawl_Success, + PMACYPP05 AS Transitioning_Affluent_Families, + PMACYPP06 AS Best_of_Both_Worlds, + PMACYPP07 AS Upscale_Diversity, + PMACYPP08 AS Living_the_Dream, + PMACYPP09 AS Successful_Urban_Refugees, + PMACYPP10 AS Emerging_Leaders, + PMACYPP11 AS Affluent_Newcomers, + PMACYPP12 AS Mainstream_Established_Suburbs, + PMACYPP13 AS Cowboy_Country, + PMACYPP14 AS American_Playgrounds, + PMACYPP15 AS Comfortable_Retirement, + PMACYPP16 AS Spacious_Suburbs, + PMACYPP17 AS New_American_Dreams, + PMACYPP18 AS Small_Town_Middle_Managers, + PMACYPP19 AS Outer_Suburban_Affluence, + PMACYPP20 AS Rugged_Individualists, + PMACYPP21 AS New_Suburban_Style, + PMACYPP22 AS Up_and_Coming_Suburban_Diversity, + PMACYPP23 AS Enduring_Heartland, + PMACYPP24 AS Isolated_Hispanic_Neighborhoods, + PMACYPP25 AS Hipsters_and_Geeks, + PMACYPP26 AS High_Density_Diversity, + PMACYPP27 AS Young_Coastal_Technocrats, + PMACYPP28 AS Asian_Hispanic_Fusion, + PMACYPP29 AS Big_Apple_Dreamers, + PMACYPP30 AS True_Grit, + PMACYPP31 AS Working_Hispania, + PMACYPP32 AS Struggling_Singles, + PMACYPP33 AS Noreasters, + PMACYPP34 AS Midwestern_Comforts, + PMACYPP35 AS Generational_Dreams, + PMACYPP36 AS Olde_New_England, + PMACYPP37 AS Faded_Industrial_Dreams, + PMACYPP38 AS Failing_Prospects, + PMACYPP39 AS Second_City_Beginnings, + PMACYPP40 AS Beltway_Commuters, + PMACYPP41 AS Garden_Variety_Suburbia, + PMACYPP42 AS Rising_Fortunes, + PMACYPP43 AS Classic_Interstate_Suburbia, + PMACYPP44 AS Pacific_Second_City, + PMACYPP45 AS Northern_Blues, + PMACYPP46 AS Recessive_Singles, + PMACYPP47 AS Simply_Southern, + PMACYPP48 AS Tex_Mex, + PMACYPP49 AS Sierra_Siesta, + PMACYPP50 AS Great_Plains_Great_Struggles, + PMACYPP51 AS Boots_and_Brew, + PMACYPP52 AS Great_Open_Country, + PMACYPP53 AS Classic_Dixie, + PMACYPP54 AS Off_the_Beaten_Path, + PMACYPP55 AS Hollows_and_Hills, + PMACYPP56 AS Gospel_and_Guns, + PMACYPP57 AS Cap_and_Gown, + PMACYPP58 AS Marking_Time, + PMACYPP59 AS Hispanic_Working_Poor, + PMACYPP60 AS Bordertown_Blues, + PMACYPP61 AS Communal_Living, + PMACYPP62 AS Living_Here_in_Allentown, + PMACYPP63 AS Southern_Small_City_Blues, + PMACYPP64 AS Struggling_Southerners, + PMACYPP65 AS Forgotten_Towns, + PMACYPP66 AS Post_Industrial_Trauma, + PMACYPP67 AS Starting_Out, + PMACYPP68 AS Rust_Belt_Poverty, + largest_profile + FROM mbforrcdb.ags_consumer_profiles + `, + + options: { + featureClickColumns: [] + } + }; + \ No newline at end of file diff --git a/src/data/layers/demos.js b/src/data/layers/demos.js new file mode 100644 index 0000000..0fef667 --- /dev/null +++ b/src/data/layers/demos.js @@ -0,0 +1,34 @@ +export default { + name: 'Consumer Demographics', + + visible: false, + + cartocss: ` + #layer { + polygon-fill: ramp([normalized], (#fbe6c5, #f5ba98, #ee8a82, #dc7176, #c8586c, #9c3f5d, #70284a), quantiles); + } + #layer::outline { + line-width: 0; + line-color: #FFFFFF; + line-opacity: 0.5; + } + `, + + query: ` + SELECT + a.*, + a.POPCY/st_area(a.the_geom) as normalized, + b.POPPY, + b.HHDPY, + b.DWLPY, + b.AGEPYMED, + b.INCPYMEDHH + FROM mbforrcdb.ags_sociodemographics a + LEFT JOIN bq_results_20190918_144209_lnde788rpg04 b ON a.geoid = b.geoid + `, + + options: { + featureClickColumns: [] + } + }; + \ No newline at end of file diff --git a/src/data/layers/index.js b/src/data/layers/index.js index 31c5d49..8a5b861 100644 --- a/src/data/layers/index.js +++ b/src/data/layers/index.js @@ -1,9 +1,17 @@ -import railaccidents from './railaccidents'; -import usstates from './usstates'; - -// import neighbourhoods from './neighbourhoods'; +import target from './target'; +import targetpoints from './targetpoints'; +import mastercard from './mastercard' +import consumer from './consumer' +import demos from './demos' +import poi from './poi' // Export order will be the layer order export default { - railaccidents + consumer, + mastercard, + demos, + target, + poi, + targetpoints + } diff --git a/src/data/layers/mastercard.js b/src/data/layers/mastercard.js new file mode 100644 index 0000000..17ce426 --- /dev/null +++ b/src/data/layers/mastercard.js @@ -0,0 +1,25 @@ +export default { + name: 'Mastercar Retail Location Insights', + + visible: false, + + cartocss: ` + #layer { + polygon-fill: ramp([sales_score], (#fbe6c5, #f5ba98, #ee8a82, #dc7176, #c8586c, #9c3f5d, #70284a), quantiles); + } + #layer::outline { + line-width: 0; + line-color: #FFFFFF; + line-opacity: 0.5; + } + `, + + query: ` + SELECT * FROM mbforrcdb.mastercard where category = 'total retail' and month = '05/01/2019' + `, + + options: { + featureClickColumns: [] + } + }; + \ No newline at end of file diff --git a/src/data/layers/poi.js b/src/data/layers/poi.js new file mode 100644 index 0000000..171fab8 --- /dev/null +++ b/src/data/layers/poi.js @@ -0,0 +1,27 @@ +export default { + name: 'Competitors', + + visible: false, + + cartocss: ` + #layer { + marker-width: 7; + marker-fill: ramp([name], (#0072CE, #04589a, #FA6304, #F2B701, #F7ED00, #800033, #E31837, #e98686), ("Walmart", "Kroger", "Home Depot", "Best Buy", "Kohls", "Costco", "K-Mart"), "="); + marker-fill-opacity: 1; + marker-allow-overlap: true; + marker-line-width: 1; + marker-line-color: #FFFFFF; + marker-line-opacity: 1; + } + `, + + query: ` + SELECT * FROM costco_usa_canada_copy + `, + + options: { + featureClickColumns: [] + } + }; + + \ No newline at end of file diff --git a/src/data/layers/railaccidents.js b/src/data/layers/railaccidents.js deleted file mode 100644 index e5b6b16..0000000 --- a/src/data/layers/railaccidents.js +++ /dev/null @@ -1,25 +0,0 @@ -export default { - name: 'Rail Accidents', - - visible: true, - - cartocss: ` - #layer { - marker-width: 7; - marker-fill: ramp([equipment_damage], (#f3e79b, #fac484, #f8a07e, #eb7f86, #ce6693, #a059a0, #5c53a5), quantiles); - marker-fill-opacity: 1; - marker-allow-overlap: true; - marker-line-width: 1; - marker-line-color: #FFFFFF; - marker-line-opacity: 1; - } - `, - - query: ` - SELECT * FROM rail_accidents - `, - - options: { - featureClickColumns: ['cartodb_id', 'railroad', 'total_damage', 'cause', 'weather', 'accident_type', 'image'] - } -}; diff --git a/src/data/layers/target.js b/src/data/layers/target.js new file mode 100644 index 0000000..3a9d81a --- /dev/null +++ b/src/data/layers/target.js @@ -0,0 +1,27 @@ +export default { + name: 'Target Isolines', + + visible: true, + + cartocss: ` + #layer { + polygon-fill: #ffffff; + polygon-opacity: 0.3; + } + #layer::outline { + line-width: 0; + line-color: #f22323; + line-opacity: 1; + } + `, + + query: ` + SELECT * FROM target_iso + `, + + options: { + featureClickColumns: ['store_name', 'id', 'formatted', 'city', 'state', 'contact', 'stores_open', 'subtype', 'timings', 'type', 'opened', 'last_remodel', 'store_features', 'zipcode', 'median_income', 'total_pop', 'pop_sqkm', 'year', 'households', 'timings', 'open_formatted', 'last_remodel_formatted', 'replace'] + } + }; + + \ No newline at end of file diff --git a/src/data/layers/targetpoints.js b/src/data/layers/targetpoints.js new file mode 100644 index 0000000..0e4ead0 --- /dev/null +++ b/src/data/layers/targetpoints.js @@ -0,0 +1,33 @@ +export default { + name: 'Target Stores', + + visible: true, + + cartocss: ` + #layer { + marker-width: ramp([sales_yearly], range(6, 18), quantiles(5)); + marker-fill: #EE4D5A; + marker-fill-opacity: 0.9; + marker-allow-overlap: true; + marker-line-width: 1; + marker-line-color: #FFFFFF; + marker-line-opacity: 1; + } + `, + + query: ` + SELECT t.*, + to_char(t.opened, 'MM/DD/YYYY') as open_formatted, + case when t.last_remodel is not null + THEN to_char(t.last_remodel, 'MM/DD/YYYY') + ELSE null END AS last_remodel_formatted, + d.sales_yearly, + replace(lower(t.store_name), ' ', '-') + FROM target t + LEFT JOIN dfan d ON t.id = d.id + `, + + options: { + featureClickColumns: ['store_name', 'id', 'formatted', 'city', 'state', 'contact', 'stores_open', 'subtype', 'timings', 'type', 'opened', 'last_remodel', 'store_features', 'zipcode', 'median_income', 'total_pop', 'pop_sqkm', 'year', 'households', 'timings', 'open_formatted', 'last_remodel_formatted', 'replace'] + } +}; diff --git a/src/data/layers/usstates.js b/src/data/layers/usstates.js deleted file mode 100644 index 245ae6d..0000000 --- a/src/data/layers/usstates.js +++ /dev/null @@ -1,25 +0,0 @@ -export default { - name: 'US States', - - visible: true, - - cartocss: ` - #layer { - polygon-fill: #6ba2dc; - polygon-opacity: 1; -} -#layer::outline { - line-width: 1; - line-color: #FFFFFF; - line-opacity: 0.5; -} - `, - - query: ` - SELECT * FROM us_states - `, - - options: { - featureClickColumns: [] - } -}; diff --git a/src/routers/AppRouter.js b/src/routers/AppRouter.js index 9107213..d4df2fe 100644 --- a/src/routers/AppRouter.js +++ b/src/routers/AppRouter.js @@ -1,6 +1,7 @@ import React from 'react'; import { BrowserRouter, Route, Switch, Link, NavLink } from 'react-router-dom'; import Page from '../components/Page'; +import Predict from '../components/Predict'; import NotFoundPage from '../components/NotFoundPage'; const AppRouter = () => ( @@ -8,7 +9,7 @@ const AppRouter = () => (
- +
diff --git a/src/styles/base/_base.scss b/src/styles/base/_base.scss index 4bda79e..a1f0ce9 100644 --- a/src/styles/base/_base.scss +++ b/src/styles/base/_base.scss @@ -5,6 +5,8 @@ html { height: 100%; } + + body { font-family: 'Roboto', sans-serif; font-size: $m-size; diff --git a/src/styles/base/_theme.scss b/src/styles/base/_theme.scss index e6f428c..f047e5a 100644 --- a/src/styles/base/_theme.scss +++ b/src/styles/base/_theme.scss @@ -1,5 +1,91 @@ :root { - // --as-color-primary: #FFA630; - // --as-color-secondary: #2E5077; - // --as-color-complementary: #D7E8BA; + --as--color--primary: #CC0000 !important; + --as--color--secondary: #fff !important; + --as--color--complementary: #969696 !important; } + +.as-toolbar a { + color: #CC0000 !important; +} + +.as-map-footer { + max-height: 360px !important; +} + +.as-toolbar { + border-bottom: 1px solid #CC0000 !important; + background-color: #fff !important; +} + +.as-toolbar__item { + border-bottom: 1px solid #CC0000 !important; + background-color: #fff !important; +} + +.as-sidebar--right { + order: 9999; + box-shadow: none !important; + border-left: 1px solid #CC0000 !important; +} + +.legend-circle { + position: relative; + min-width: 12px; + height: 12px; + margin-right: 2px; + margin-left: 2px; + border-radius: 50%; +} + +.as-display { + margin: 0px 0px 4px; +} + +.as-map-footer { + border-top: 1px solid #CC0000 !important; +} + +.as-time-series-widget { + width: 100%; + + height: 100% !important; + background: red; + } + +// .as-widget-header__header { +// margin-left: 32px !important; +// } +.as-widget-selection__selection { + margin-left: 32px !important; +} + +.as-infowindow { + min-width: 320px; +} + +.as-legend-color-continuous-polygon { + min-width: 150px !important; +} + +.as-legend-color-continuous-polygon--wrapper { + min-width: 150px !important; +} + +.as-legend-color-continuous-polygon--labels { + min-width: 150px !important; +} +.as-legend-color-continuous-polygon--horizontal { + min-width: 150px !important; +} + +.as-legend-size-bins-line { + min-width: 150px !important; +} + +.as-legend-size-bins-line--horizontal { + min-width: 150px !important; +} + +.as-legend-size-bins-line--wrapper { + min-width: 150px !important; +} \ No newline at end of file