Skip to content

Commit

Permalink
Add plotter, add markers, dates. WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
metasyn authored and Xander Johnson committed Feb 2, 2018
1 parent 88ccaa4 commit 7cd3648
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 27 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"jquery": "^3.3.1",
"mapbox-gl": "^0.44.0",
"react": "^16.2.0",
"react-dom": "^16.2.0"
"react-dom": "^16.2.0",
"prop-types": "^15.6.0"
}
}
18 changes: 18 additions & 0 deletions src/css/stylish.css
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,21 @@ button {
.vex-content h1 {
font-size: 32px;
}

/* Mapbox GL Stuff */

.marker {
background-image: url('../img/marker.png');
background-size: contain;
width: 30px;
height: 77px;
cursor: pointer;
}

.mapboxgl-popup {
max-width: 200px;
}

.mapboxgl-popup-content {
font-family: 'Open Sans', sans-serif;
}
Binary file added src/img/marker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 18 additions & 7 deletions src/js/app.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
import React, { Component } from 'react';
import { render } from 'react-dom';

import Parser from './components/parser';
import Util from './components/util';
import ShowMap from './components/map';
import Parser from './components/Parser';
import Dates from './components/dates/Dates';
import ShowMap from './components/Map';

import '../css/stylish.css';

class Application extends Component {
static plot() {
static prepare() {
const parsed = new Parser().parseData();

// keys: organized, dates
parsed.then((data) => {
Util.populateDates(data.dates);
// Pop modal?
// TODO

// Add the dates
const dateEl = document.getElementById('date-selector-container');
render(<Dates dates={data.dates} />, dateEl);

// Add the shows
const mapEl = document.getElementById('app');
render(<ShowMap geojson={data.geojson} />, mapEl);
console.log(data.geojson)
});
}

render() {
Application.plot();
Application.prepare();
return (
<ShowMap />
<div />
);
}
}
Expand Down
69 changes: 69 additions & 0 deletions src/js/components/Plotter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export default class Plotter {
static plotShows(organized) {
return organized;
}
}

/*
function plotShows(geojson) {
// update function for coordinates infobox
window.onmove = function onmove() {
// Get the map bounds - the top-left and bottom-right locations.
const inBounds = [],
bounds = map.getBounds();
clusterGroup.eachLayer((marker) => {
// For each marker, consider whether it is currently visible by comparing
// with the current map bounds.
if (bounds.contains(marker.getLatLng()) && selectedDatesList.indexOf(marker.feature.properties.date) !== -1) {
const feature = marker.feature;
const coordsTemplate = L.mapbox.template('{{properties.date}} - {{properties.venue}} |{{#properties.bands}} {{.}} |{{/properties.bands}}{{properties.details}}', feature);
inBounds.push(coordsTemplate);
}
});
// Display a list of markers.
inBounds.reverse();
document.getElementById('coordinates').innerHTML = inBounds.join('\n');
};
// attach data
const myLayer = L.mapbox.featureLayer(geojson);
// make clustergroup
const clusterGroup = ModifiedClusterGroup();
// add features
clusterGroup.addLayer(myLayer);
overlays = L.layerGroup().addTo(map);
// add cluster layer
// overlays are multiple layers
// add in showShows()
showShows();
// for each layer in feature layer
myLayer.eachLayer((e) => {
const marker = e;
const feature = e.feature;
// Create custom popup content
const popupContent = L.mapbox.template('<h1> {{properties.venue}} </h1><br><h3> {{properties.date}} </h3><br><h2> {{#properties.bands}} - {{.}} <br> {{/properties.bands}} </h2><br><h2> {{properties.details}} </h2><br>', feature);
marker.bindPopup(popupContent, {
closeButton: true,
minWidth: 320,
});
});
map.on('move', onmove);
// call onmove off the bat so that the list is populated.
// otherwise, there will be no markers listed until the map is moved.
window.onmove();
}
function ModifiedClusterGroup() {
return new L.MarkerClusterGroup({
spiderfyOnMaxZoom: true,
maxClusterRadius: 1,
spiderfyDistanceMultiplier: 3,
});
}
*/
24 changes: 24 additions & 0 deletions src/js/components/dates/DateSelector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class DateSelector extends Component {
render() {
return (
<div>
<input
type="checkbox"
name="filters"
onClick={this.props.showShows}
value={this.props.date}
defaultChecked
/> { this.props.date }
</div>
);
}
}

DateSelector.propTypes = {
date: PropTypes.string.isRequired,
showShows: PropTypes.func.isRequired,
};

33 changes: 33 additions & 0 deletions src/js/components/dates/Dates.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';

import DateSelector from './DateSelector';

export default class Dates extends Component {
constructor(props) {
super(props);
this.makeDateSelectors = this.makeDateSelectors.bind(this);
this.showShows = this.showShows.bind(this);
}

// eslint-disable-next-line
showShows(){}

makeDateSelectors(dates) {
const selectors = [];
for (let d = 0; d < dates.length; d += 1) {
const selector = (<DateSelector
showShows={this.showShows}
date={dates[d]}
key={dates[d]}
/>);
selectors.push(selector);
}
return selectors;
}
render() { return this.makeDateSelectors(this.props.dates); }
}

Dates.propTypes = {
dates: PropTypes.array.isRequired,
};
60 changes: 52 additions & 8 deletions src/js/components/map.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';

import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

import Venues from '../../data/venues.json';

mapboxgl.accessToken = 'pk.eyJ1IjoibWV0YXN5biIsImEiOiIwN2FmMDNhNTRhOWQ3NDExODI1MTllMDk1ODc3NTllZiJ9.Bye80QJ4r0RJsKj4Sre6KQ';

class ShowMap extends Component {
constructor(Props) {
super(Props);
constructor(props) {
super(props);

// Centered on SF
this.state = {
lng: -122.416,
lat: 37.76,
zoom: 13,
};

this.bindMap = this.bindMap.bind(this);
}

componentDidMount() {
Expand All @@ -27,33 +32,72 @@ class ShowMap extends Component {
zoom,
});

// Add locator control
map.addControl(new mapboxgl.GeolocateControl({
positionOpionts: {
enableHighAccuracy: true,
},
trackUserLocation: true,
}));

// Add the actual shows
map.on('load', () => {
map.addSource('shows', {
type: 'geojson',
data: this.props.geojson,
});
});

map.on('move', () => {
const { lng, lat } = map.getCenter();
const center = map.getCenter();

this.setState({
lng: lng.toFixed(4),
lat: lat.toFixed(4),
lng: center.lng.toFixed(4),
lat: center.lat.toFixed(4),
zoom: map.getZoom().toFixed(2),
});
});

ShowMap.plotMarkers(this.props.geojson, map);
}

render() {
const { lng, lat, zoom } = this.state;
bindMap(el) {
this.mapContainer = el;
}

static plotMarkers(geojson, map) {
geojson.features.forEach((marker) => {
const el = document.createElement('div');
el.className = 'marker';

const lngLat = Venues[marker.geometry.coordinates];
const popup = new mapboxgl.Popup({ offset: 25 }).setHTML(marker.properties.showHTML)

if (lngLat) {
try {
new mapboxgl.Marker(el)
.setLngLat(lngLat)
.setPopup(popup)
.addTo(map);
} catch (e) {
console.log(e)
}
}
});
}


render() {
return (
<div>
<div ref={el => this.mapContainer = el} className="absolute top right left bottom" />
<div ref={this.bindMap} className="absolute top right left bottom" />
</div>
);
}
}

ShowMap.propTypes = {
geojson: PropTypes.object.isRequired,
};

export default ShowMap;
66 changes: 65 additions & 1 deletion src/js/components/parser.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import $ from 'jquery';

import Venues from '../../data/venues.json';
import getEditDistance from './Util';

export default class Parser {
constructor() {
this.yql = Parser.makeYQL();
Expand All @@ -11,7 +14,8 @@ export default class Parser {
const results = Parser.parseHTMLtoDOM(success);
const dates = Parser.getDates(results);
const organized = Parser.sortByDate(results, dates);
return { organized, dates };
const geojson = Parser.geojsonify(organized);
return { organized, geojson, dates };
})
.catch(e => Error((e)));
}
Expand Down Expand Up @@ -68,4 +72,64 @@ export default class Parser {

return organized;
}

static geojsonify(data) {
const features = [];
const dateKeys = Object.keys(data);

// loop through dates
for (let i = 0; i < dateKeys.length; i += 1) {
// loop through shows
for (let j = 0; j < data[dateKeys[i]].length; j += 1) {
const showData = data[dateKeys[i]][j];
const venueList = Object.keys(Venues);

// check for misspellings
if (!Venues[showData.venue]) {
try {
for (let v = 0; v < venueList.length; v += 1) {
const misspelled = showData.venue.replace(/\W/g, '');
const spelledCorrect = venueList[v].replace(/\W/g, '');
const editDistance = getEditDistance(misspelled, spelledCorrect);
if (editDistance <= 3) {
console.log(`"${showData.venue}" has been replaced with "${venueList[v]}"`);
showData.venue = venueList[v];
}
}
} catch (e) {
console.log('Missing Venue?', e);
}
}

const showString = `${dateKeys[i]} - ${showData.venue} | ${showData.bands.join(' |')} | ${showData.details}`;
const showHTML = `<h1> ${showData.venue} </h1><br/><h3> ${dateKeys[i]} </h3><br/><h2>${showData.bands.join(' |')}<br/> ${showData.details}`;

const show = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [showData.venue] || [-122.422960, 37.826524],
},
properties: {
date: dateKeys[i],
venue: showData.venue,
bands: showData.bands,
details: showData.details.replace(/ ,/g, ''), // fucking commas
showString,
showHTML,
},
};

// add show to features array
features.push(show);
}
}

// format for valid geojson
const geojson = {
type: 'FeatureCollection',
features,
};
return geojson;
}
}
Loading

0 comments on commit 7cd3648

Please sign in to comment.