Skip to content

Commit

Permalink
Merge pull request #97 from giftofgrub/checkPOIs
Browse files Browse the repository at this point in the history
POI Manager
  • Loading branch information
Kyle-Falconer authored Jan 29, 2020
2 parents 2c0a7da + 608576d commit 0819c71
Show file tree
Hide file tree
Showing 13 changed files with 602 additions and 31 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,29 @@ The following steps are involved in updating the artwork data:
4. **Lookup addresses with [OSM/Nominatim](https://wiki.openstreetmap.org/wiki/Nominatim)** - Run `npm run lookupAddresses`. This will use the artwork address field to attempt to get the geolocation (latitude and longitude). The output of this command will indicate if there are failures to lookup the address. If you know the location, then add an override for it in the `artworks-overrides.json` and re-run step 2, then re-run step 4.
5. **Copy the artwork JSON data into art.js** - Finally, run `npm run moveArtToOutputDir` to copy the contents of `artwork-data/consolidated-artworks.json` into `js/art.js`, prefixing the content with `const art = `. This is the final step and is needed before the changes can be visible in the web application.

#### Managing the live data:

***POI Manager***

The POI Manager exists to better assist with the creation and management of all artworks or points of interest. Because the primary source of data is the scraped artworks from the San Jose site, there exists some inconsistensies and each site needs to be vetted. The intent is to have all POIs in the **artwork-data/managedPOIs.json** file be complete with information before being shown by the [heartOfTheValley](https://www.codeforsanjose.com/heartofthevalley/) application.

***Running the POI Manager***
1. From the project directory, Open POI Manager. This opens up a local server using assets from the **assetsPOIManager** folder.
```bash
npm run managePOIs
```
2. Go to http://localhost:3000. Use the POI Manager.
3. Once done, from the terminal window used to run the POI Manager, hit Ctrl + C.

***Using the POI Manager***
1. On the index page, the left hand side will have all the scraped artworks that are not yet live. The right hand side will have all the artworks that have been vetted.
![Index Page](/img/POIManagerIndex.png)
2. Click on a scraped artwork to open up the Edit POI Page or click on "Create New POI" to open up the New POI Page.
3. Once on the Edit/New POI page, include all details. You can view how the artwork will appear on the map area to the right. Click on the "Refresh Map & Popup" button to see any changes that have been made.
![New/Edit Page](/img/POIManagerEdit.png)
4. Click "Submit POI" to have this sent to the **artwork-data/managedPOIs.json** file to be consumed by the [heartOfTheValley](https://www.codeforsanjose.com/heartofthevalley/) application.


#### Running the project locally
This project is completely static, so simply open `index.html` in Chrome or Firefox.

Expand Down
25 changes: 25 additions & 0 deletions artwork-data/managedPOIs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
"-121.80158876559241",
"37.2992397"
]
},
"properties": {
"title": "8 Minutes",
"artist": "Merge Conceptual Design",
"address": "1924 Yerba Buena Road",
"city": "San Jose",
"state": "CA",
"postalCode": "95121",
"image": "https://sanjoseca.gov/Home/ShowPublishedImage/4878/636714226994330000",
"description": "The National Fire Protection Association recommends a target of 8 minutes as “standard response time” for emergency calls. The artwork 8 Minutes approaches the topic of the 8-minute response in three ways:\n\n A visual measurement of 8 minutes,\n A conveyor of Fire Station 24th continuous efforts on behalf of the community, and\n Connecting the community to the station via the eight minute duration.\n\nAll the elements complement each other to create a rich understanding of time in the context of Fire Station 24.\nThe first element is the LED Countdown Timer placed over the apparatus bay. The timer is connected to the fire pager system; when a response call is received it automatically starts an 8-minute counter that travels as a light across the sign. After the 8-minute light completes its travel across the sign, the \"call time\" is added to a list of the 8 most recent call times displayed on the sign. The second and third components of the artwork are a series of tiles in the fire station entryway. One set of tiles is a compilation of text about the number 8 and 8-minute duration. For example: “A fallen or lying down 8 is used to represent infinity in mathematics.” The third component is a series of 8-minute time-lapsed community photographs that depict every day activities to the same time frame of the recommended standard response time – to provide visitors to the station a visceral sense of the anxiety-filled 8 minutes awaiting emergency response versus the same 8-minutes washing a car, skateboarding or barbecuing.",
"sourceURL": "https://sanjoseca.gov/Home/Components/FacilityDirectory/FacilityDirectory/2477/1396",
"sourceOfInformation": "Created by POI Manager"
},
"id": "art_fb3517580c972354c2b65afcba98c0aa0174184a4b77b1204b870e41046d1b05"
}
]
139 changes: 139 additions & 0 deletions assetsPOIManager/helpersPOIManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
mapboxgl.accessToken = "pk.eyJ1IjoieWNob3kiLCJhIjoiY2pmOTYwdzZ5MG52dDJ3b2JycXY4ZDU5ciJ9.m9H_Mqu1b42AObg_u_tjpA";

let map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/light-v9",
center: [-121.893028, 37.33548], // position in long, lat format
zoom: 10,
dragPan: true, // If true , the "drag to pan" interaction is enabled (see DragPanHandler)
trackResize: true, // If true, the map will automatically resize when the browser window resizes.
doubleClickZoom: true, //If true double click will zoom
keyboard: true //If true will enable keyboard shortcuts
});

map.on("load", function(e) {
let current = getCurrentVariables();
let {longitude, latitude, address, city, state, postalCode} = current;
if (longitude && latitude) {
// coordinates are present
mapFlyToCurrent();
} else {
// coordinates are not present.
if (address && city && state && postalCode) {
queryNominatim();
} else {
console.log("artwork does not have coordinates or a valid address")
}
}
});

function queryNominatim() {
let current = getCurrentVariables();
const nominatimAPI = `https://nominatim.openstreetmap.org/?`;
let query = {
params:{
q: current.address + ", " + current.city + ", " + current.state + ", " + current.postalCode,
format: "geocodejson",
limit: "1",
}
}
axios
.get(nominatimAPI, query)
.then(
function(response) {
let [long, lat] = response.data.features[0].geometry.coordinates;
document.getElementById("longitude").value = long;
document.getElementById("latitude").value = lat;
mapFlyToCurrent();
})
.catch(
function(error) {
alert("Error getting coordinates from Nominatim.\nPlease make sure to have a legitimate address, city, and state.")
});
}

function submitPOI() {
let current = getCurrentVariables();
let {title, artist, address, description, city, state, postalCode, image, sourceURL, id, latitude, longitude, pageType} = current;
if (!current.latitude || !current.longitude) {
alert("Missing Coordinates. Cannot Submit");
return
}
if (!title || !artist || !description ) {
alert("Missing Details. Cannot Submit")
return
}

let pathTo = "/POI";
if (pageType === "Edit") {pathTo = `/POI/${id}`}
axios.post(pathTo, {
title, artist, address, description, city, state, postalCode, image, sourceURL, id, latitude, longitude
})
.then( response => {
const statusCode = response.status;
if (statusCode === 201) {
console.log(response.data.artwork);
alert(
"successfully submitted artwork\n" +
response.data.artwork.properties.title +
"\nby\n" +
response.data.artwork.properties.artist);
}
else {
alert("Failed artwork creation")
}
window.location = ("/");
}).catch(error => {
alert(error);
window.location = ("/");
})
}

var popup;

function mapFlyToCurrent() {
let current = getCurrentVariables();
let {longitude, latitude, title, artist, address, city, state, postalCode, image, description} = current;
if (!latitude || !longitude) { alert("Unable to fly to artwork. Missing coordinates"); return }
map.flyTo({
center: [longitude, latitude],
zoom: 15
});
if (popup) {popup.remove()}
popup = new mapboxgl.Popup({ closeOnClick: true, closeButton: true, anchor: "top" })
.setLngLat([longitude, latitude])
.setHTML(
`<div style="width: 300px;">
<h3>${title}</h3>
<p>by ${artist}</p>
<p>Address: ${address}, ${city}, ${state}, ${postalCode}</p>
<div>
<img style="width: 300px" src=${image}>
</div>
<div><strong>Description: </strong><p>${description}</p></div>
</div>`
).addTo(map);
}

function getCurrentVariables() {
let lib = {
title: 'title',
artist: 'artist',
description: 'description',
address: 'address',
city: 'city',
state: 'state',
postalCode: 'postalCode',
image: 'image',
sourceURL: 'sourceURL',
id: 'artworkId',
latitude: 'latitude',
longitude: 'longitude',
};
// let title, artist, description, address, city, state, postalCode, image, sourceURL, id, longitude, latitude;
Object.keys(lib).forEach( (key) => {
lib[key] = document.getElementById(lib[key]).value;
})
lib.pageType = document.getElementById("pageType").innerText;
return lib;
}
37 changes: 37 additions & 0 deletions assetsPOIManager/models/POIManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const { PATH_SCRAPED_ARTWORKS, PATH_MANAGED_POIs } = require('../../config/file-paths');
const fs = require('fs');

class POIManager {
constructor(data) {
this.scrapedArtworks = {};
this.managedArtworks = {};
}

refreshArtworks() {
const managedArr = require(PATH_MANAGED_POIs);
const scrapedArr = require(PATH_SCRAPED_ARTWORKS);
managedArr.forEach((artwork) => {
this.managedArtworks[artwork.id] = artwork;
});
scrapedArr.forEach( (artwork) => {
if (!this.managedArtworks[artwork.id]) {
this.scrapedArtworks[artwork.id] = artwork;
} else if (this.managedArtworks[artwork.id]) {
delete this.scrapedArtworks[artwork.id]
}
});
}

writeArtworkToFile(artworkToSubmit) {
this.managedArtworks[artworkToSubmit.id] = artworkToSubmit;
const managedArtworksString = JSON.stringify(Object.values(this.managedArtworks), null, 2)
return new Promise(resolve => {
fs.writeFile(PATH_MANAGED_POIs, managedArtworksString, {flags: "r+"}, err => {
if (err) { return reject(err) };
resolve();
})
}).catch(console.error);
}
}

module.exports = POIManager;
65 changes: 65 additions & 0 deletions assetsPOIManager/templates/index.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>

<!-- Include style CSS and JavaScript -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>

<div class="container instructions-area w-100">
<h1>POIs Index</h1>
<p>Please check details of each POI / Artwork. Use responsibly.</p>
</div>

<div class="container">
<div class="row">
<div class="col-md-6">
<h4>Scraped Artworks</h4>
<ol class="scraped-artworks list-group">
<% scrapedArtworks.forEach((artwork) => { %>
<li style="border: 1px solid gray; margin-bottom: 1px; padding-left: 2px;">
<a href=<%="/POI/" + artwork.id%> >
<%= artwork.properties.title%>
</a>
</li>
<% }) %>

</ol>
</div>
<div class="col-md-6">
<div style="display: flex; justify-content: space-between;">
<h4 style="display:inline">Managed POIs</h4>
<form action="/POI/new" method="get">
<button class="btn" style="border: 1px solid black; margin-left: 5px;">Create New POI</button>
</form>
</div>

<ol class="scraped-artworks list-group">
<% managedArtworks.forEach((artwork) => { %>
<li style="border: 1px solid gray; margin-bottom: 1px;">
<a href=<%="/POI/" + artwork.id%> >
<%= artwork.properties.title%>
</a>
</li>
<% }) %>

</ol>
</div>
</div>
</div>
</body>

</html>
Loading

0 comments on commit 0819c71

Please sign in to comment.