# Project export: Oski Rescue

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: Oski Rescue is a web app that utilizes the Google Maps API to create a simple but effective way to view emergency resources on and around the UC Berkeley Campus.
- Devpost: https://devpost.com/software/oski-rescue
- GitHub: https://github.com/nekolocation/calhacks23
- Demo: https://nekolocation.github.io/hackathon/oski-rescue
- Team: 4 GitHub contributor(s) — Jovin Valdez (10 commits), nekolocation (4 commits), thesageak (2 commits), Arjun Rewari (1 commits)

## Devpost submission (written by the team)

### Overview

Our Goal Our project was inspired by safety concerns traveling around campus (especially at night). We couldn't find a resource that consolidated safety info around campus... so we made one ourselves! Our mission was to make a site that helps the user better understand where they can travel, in an emergency, to get timely help. As well, we wanted to make a website that loads very quickly, in case anyone used the website in an actual emergency.

### What it does

Our website shows the location of Emergency Blue Light Telephones across the UC Berkeley Campus, as well as Emergency phones and Police Stations in the surrounding area. As an additional feature, we added a button for easy access to call the UC Berkeley Campus Police.

### How we built it

We collaborated on our design in Figma, building out a vision of the website's skeleton and aesthetics. We also used Figma to create some of the image assets (the icons that show up on the map, and the logo). We created this website using the Google Maps API and associated TypeScript to tailor the map features to only what's relevant. Challenges Our team came into this project with limited time and energy, arriving to the event late due to midterms. As such, we missed the opening ceremony and many of the useful info sessions; we had to talk to staff, other teams, and do research to figure out what the guidelines were for the event. There were many learning hurdles involved in approaching this project: Going into the project, most team members had very limited familiarity with TypeScript, API usage, and front-end development Our team had limited experience using Github, especially collaboratively Github pages, which we use to host the website, proved confusing when attempting to host a live site while hiding the API key Github pages, which we use to host the website, proved confusing when attempting to host a live site while hiding the API key As a result of this final point, we were surprised to see the live website not working as we expected, due to limited back-end knowledge on how to hide API keys from public eyes.

### Accomplishments we're proud of

For most of our team, this was our first Hackathon! We're happy to have made it to the end despite how tired we all were. This was our first time working as a group in a project of this scale, tackling something outside all of our technical abilities.

### What we learned

By the end of the project, we've become exceptionally more comfortable with all of the aforementioned difficulties. We were able to figure out how to get set up with an API, integrate it into our project, and customize it to suit our needs. We became comfortable with using version control as a collaborative tool, and with picking up new technologies during the development process.

### What's next

The website would function well as an app, which we're interested in creating! There are a number of other features that we were interested in implementing (but didn't have the time) including live user-submitted incident reports, a tool to fake a received telephone call, and a way to highlight the nearest Emergency Blue Light Phone. (First thing's first though— getting the live website working over the summer!)

## README (from the GitHub repository)

# Welcome to Oski Rescue! 🐻

Oski Rescue is a web app that utilizes the Google Maps API to create a simple but effective way to view emergency resources on and around the UC Berkeley Campus. This is accomplished by combining JavaScript & TypeScript functionality with information sourced online to pinpoint📍	Blue Light Emergency Telephones 📞.

## Detected evidence (automated analysis)

Indexed codebase: 7 recognized source files, 15 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (13 of 13)

```
.env
.eslintrc.json
.gitignore
assets/css/style.css
cloud_shell_instructions.md
env.d.ts
index.html
index.ts
package.json
README.md
sandbox.config.json
tsconfig.json
vite.config.js
```

### Dependencies

- package.json: @types/google.maps@^3.53.5, typescript@^5.2.2, vite@^4.4.9

### Recent commits (newest first)

- changed sumsum
- added medical markers
- Merge branch 'main' of https://github.com/nekolocation/calhacks23
- removed business markers, added emergency telephones and more blue lights
- Merge branch 'main' of https://github.com/nekolocation/calhacks23
- added blue light in front of li ka shing
- removed all business markers
- Style changes to develop and flesh out an identity for the website
- improved center button, removed satellite and streetview options, moved zoom buttons to the top right
- Merge branch 'main' of https://github.com/nekolocation/calhacks23
- improved center button, removed satellite and streetview options, moved zoom buttons to the top right
- added button to center the map to campus and removed streetview button
- added button to center the map to campus
- added emojis to readmd
- Sorry for being scuffed
- Set-up for next part
- Better late than never

## Key source files (fetched from GitHub, selected and truncated for size)

### cloud_shell_instructions.md

```markdown
# Run a Google Maps Platform JS sample in Google Cloud Shell

<walkthrough-tutorial-duration duration="10"/>

## Introduction

This tutorial will walk through the process of running a web application from
the sample using TypeScript and Vite.

For feedback related to this sample, please open a new issue on
[GitHub](https://github.com/googlemaps/js-samples/issues).

Click the **Start** button to continue.

## Install and run the application

Open Cloud Shell by clicking the
<walkthrough-cloud-shell-icon></walkthrough-cloud-shell-icon> button in the
navigation bar in the upper-right corner of the console.

To install the application and its requirements, run the following command.

```bash
npm i
```

After installation, Vite can be called using the following command.

```bash
npm start -- --port=8080
```

For configuration of Vite, check
<walkthrough-editor-open-file filePath="vite.config.js">vite.config.js</walkthrough-editor-open-file>.

## Enable Web Preview in the Google Cloud Shell

Once your app is built (should take a few moments), you can launch it via
<walkthrough-spotlight-pointer target="cloudshell" spotlightId="devshell-web-preview-button">Web
Preview button</walkthrough-spotlight-pointer> using the **port 8080**.

## Update the Google Maps API key

The application is currently using the
<walkthrough-editor-open-file filePath=".env">.env</walkthrough-editor-open-file>
file to embed the API key in the HTML document. This is a temporary key and is
not valid for production usage.

The key can be replaced by following these instructions to
[get an api key](https://developers.google.com/maps/documentation/javascript/get-api-key).

After changing the key, the Webpack server must be restarted with the following
command:

```bash
npm start -- --port=8080
```

## Inspect and edit the application files

There are three main files for this sample:

*   <walkthrough-editor-open-file filePath="index.ts">src/index.ts</walkthrough-editor-open-file>
*   <walkthrough-editor-open-file filePath="index.html">src/index.html</walkthrough-editor-open-file>
*   <walkthrough-editor-open-file filePath="style.css">src/style.css</walkthrough-editor-open-file>

Try editing the <walkthrough-editor-open-file filePath="index.ts">src/index.ts</walkthrough-editor-open-file> file and see how the web application in the web preview immediately reloads with the changes.

## Conclusion

<walkthrough-conclusion-trophy></walkthrough-conclusion-trophy>

Congratulations! You've just launched a web application using the Google Maps
Platform JS API.
```

### package.json

```
{
  "name": "map-simple",
  "description": "Samples for Google Maps JavaScript",
  "version": "2.1.4",
  "keywords": [
    "google",
    "javascript",
    "maps",
    "samples"
  ],
  "homepage": "https://github.com/googlemaps/js-samples#readme",
  "bugs": {
    "url": "https://github.com/googlemaps/js-samples/issues"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/googlemaps/js-samples.git"
  },
  "files": [],
  "license": "Apache-2.0",
  "scripts": {
    "dev": "vite",
    "start": "vite",
    "build": "vite build --outDir dist --base './'",
    "test": "tsc --no-emit",
    "preview": "vite preview"
  },
  "devDependencies": {
    "@types/google.maps": "^3.53.5",
    "typescript": "^5.2.2",
    "vite": "^4.4.9"
  },
  "private": true,
  "dependencies": {}
}
```

### index.ts

```typescript
/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */


let map: google.maps.Map;


const mapStyling = [
  {
    featureType: "poi.business",
    elementType: "labels.text",
    stylers: [{ visibility: "off" }],
  },
  {
    featureType: "poi.business",
    elementType: "labels.icon",
    stylers: [{ visibility: "off" }],
  },
  {
    featureType: "poi.school",
    elementType: "labels.text",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.school",
    elementType: "labels.icon",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.park",
    elementType: "labels.text",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.park",
    elementType: "labels.icon",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.government",
    elementType: "labels.text",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.government",
    elementType: "labels.icon",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.medical",
    elementType: "labels.text",
    stylers: [{ visibility: "on" }],
  },
  {
    featureType: "poi.medical",
    elementType: "labels.icon",
    stylers: [{ visibility: "on" }],
  },
  
];


async function initMap(): Promise<void> {

  const { Map } = await google.maps.importLibrary("maps") as google.maps.MapsLibrary;
  
  const bayAreaBounds = new google.maps.LatLngBounds(
    new google.maps.LatLng(37.8541, -122.2785), // Southwest corner
    new google.maps.LatLng(37.8846, -122.231817)  // Northeast corner
  );
  
  
  map = new Map(document.getElementById("map") as HTMLElement, {
    center: { lat: 37.8720, lng: -122.2595 },
    zoom:15,
    restriction: {
      latLngBounds: bayAreaBounds,
      strictBounds: true
    },
    streetViewControl: false,
    mapTypeControl: false,
    fullscreenControl: false,
    zoomControlOptions: {
      position: google.maps.ControlPosition.TOP_RIGHT,
    },
    styles: mapStyling,
  });

  const berkeley: google.maps.LatLngLiteral = { lat: 37.8720, lng: -122.2595 };
  
function createCenterControl(map) {
  const controlButton = document.createElement('button');

  // Set CSS for the control.
  controlButton.style.backgroundColor = '#fff';
  controlButton.style.border = '2px solid #fff';
  controlButton.style.borderRadius = '3px';
  controlButton.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
  controlButton.style.color = 'rgb(25,25,25)';
  controlButton.style.cursor = 'pointer';
  controlButton.style.fontFamily = 'Roboto,Arial,sans-serif';
  controlButton.style.fontSize = '16px';
  controlButton.style.lineHeight = '38px';
  controlButton.style.margin = '8px 0 22px';


  // controlButton.style.marginTop = '700px';

  controlButton.style.padding = '0 5px';
  controlButton.style.textAlign = 'center';

  controlButton.textContent = 'Center Map to Campus';
  controlButton.title = 'Click to recenter the map';
  controlButton.type = 'button';

  controlButton.addEventListener('click', () => {
    map.setCenter(berkeley);
  });

  return controlButton;
}


  // Create the DIV to hold the control.
  const centerControlDiv = document.createElement('div');
  // Create the control.
  const centerControl = createCenterControl(map);
  // Append the control to the DIV.
  centerControlDiv.appendChild(centerControl);

  map.controls[google.maps.ControlPosition.TOP_CENTER].push(centerControlDiv);


  // Create and add multiple markers in a loop
  const stationMarkerData = [
    // { coord },
    { lat: 37.87022691, lng: -122.2620718, title: "Frank Schlessinger Way Blue Light" },
    { lat: 37.8725741, lng: -122.2649495, title: "Li Ka Shing Blue Street Light Back" },
    { lat: 37.8726264828075, lng: -122.26547418825, title: "Li Ka Shing Blue Street Light Front" },
    { lat: 37.87350262, lng: -122.257392829701, title: "Evans Hall Blue Light"},
    { lat: 37.8736091, lng: -122.255941239326, title: "University Dr Blue Light" },
    { lat: 37.87414331568, lng: -122.256761980576, title: "Donner Lab Blue Light" },
    { lat: 37.8707578668163, lng: -122.263130018665, title: "Campanile Way Blue Light" },
    { lat: 37.8738965080177, lng: -122.261185422672, title: "Haviland Hall Blue Light" },
    { lat: 37.8712115671769, lng: -122.257007412694, title: "Morrison Hall Blue Light" },
    { lat: 37.8718675639867, lng: -122.257333025121, title: "100 South Dr Blue Light" },
    { lat: 37.870843244931, lng: -122.253532120054, title: "Boalt Hall School of Law Parking Blue Light" },
    { lat: 37.8696702, lng: -122.2549765, title: "North Field Blue Light" },
    { lat: 37.8698842104674, lng: -122.257530613784, title: "South Hall Rd Blue Light" },
    { lat: 37.8708037440816, lng: -122.26532438518, title: "Crescent Lawn Blue Light" },
    { lat: 37.8757863, lng: -122.2592305, title: "Soda Hall Blue Light" },
    { lat: 37.8753682444323, lng: -122.258177036861, title: "2600 Hearst Ave Blue Light" },
    { lat: 37.8688206326285, lng: -122.259886936391, title: "MLK Building Blue Light" },
    { lat: 37.8760131093726, lng: -122.256440284729, title: "Foothill Student Housing Blue Light 1" },
    { lat: 37.8766022427145, lng: -122.256135178904, title: "Foothill Student Housing Blue Light 2" },
    { lat: 37.8758689029305, lng: -122.257093112682, title: "Upper Parking Structure Blue Light" },
    { lat: 37.8734157253284, lng: -122.260505907456, title: "East Asian Libary Blue Light" },
    { lat: 37.86417424736154, lng: -122.24912641342158, title: "Clark Kerr Blue Light"},
    { lat:37.86717200903644, lng:-122.2563850792815, title: "Crossroads Blue Light 1"},
    { lat:37.86648046270518, lng:-122.2557879431738, title: "Crossroads Blue Light 2"},
    { lat:37.86639291585703, lng:-122.25630375123609, title: "Casa Bonita Blue Light"},
    { lat:37.86720070837783, lng:-122.25736023751256, title: "Channing-Bowditch Apartments Blue Light"},
    { lat:37.86703300884144, lng:-122.25738528236063, title: "Martinez Commons Blue Light"},
    {
[truncated — 2777 more characters]
```

### vite.config.js

```javascript
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  server: {
    hmr:
      process.env.CODESANDBOX_SSE || process.env.GITPOD_WORKSPACE_ID
        ? 443
        : undefined,
  },
});

```

### env.d.ts

```typescript
/// <reference types="vite/client" />

/**
 *  @external https://vitejs.dev/guide/env-and-mode.html
 */
interface ImportMetaEnv {
  readonly VITE_GOOGLE_MAPS_API_KEY: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

```

### index.html

```html
<!doctype html>
<!--
 @license
 Copyright 2019 Google LLC. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0
-->
<html>
  <head>
    <title>Oski Rescue</title>
    <meta content="Oski Rescue" property="og:title" />

    <meta name="description" content="Emergency info, at your fingertips."/>
    <meta name="theme-color" content="#559CEF"/>
    <link rel="icon" href="assets/img/blorb.png">

    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>

    <link rel="stylesheet" type="text/css" href="assets/css/style.css" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Karla|Kanit">

    <script type="module" src="./index.ts"></script>
  </head>

  <body>
    <div id="container">
      <div id="head-bar">
        <div id="logo-text">Oski Rescue</div> <img src="assets/img/Paw.png" height="35" alt="Paw icon">
      </div>
  
      <div id="map"></div>
  
      <a class="callButton" href="tel:5106423333"><div id="emergencyCall">Call UCPD</div></a>
      <!-- prettier-ignore -->
      <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
          ({key: "AIzaSyCZpFThh0UThCVKne9oMSkLOUT1oTp1eZc", v: "weekly"});</script>
    </div>
    
  </body>
</html>
```

### assets/css/style.css

```css
/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */

 :root{
  --DarkBlue: #0d4b7b;
  --LightBlue: #3498db;
 }


#container {
  display: flex;
  flex-direction: column;

  position: relative;
  height: 100%;
  width: 100%;
}

 #head-bar {
    display: flex;
    flex-direction: row;
    top: 0;
    left: 0;
   
    box-sizing: border-box;
    padding-left: 22px;
    padding-top: 22px;

    height: 60px;
    width: 100%;
    background-color: var(--DarkBlue);

    justify-content: left;
    align-items: center;
    text-align: center;
 }

 #logo-text {
  display: flex;

  font-family: 'Kanit', serif;
  letter-spacing:3px;
  color: white;
  font-size: 33px;
  text-shadow: 3px 3px 3px black;
 }
 
#head-bar img {
  margin-left: 10px;
}



 #map {
  box-sizing: border-box;

  position: relative;
  height: 100%;
  
  border: 24px solid var(--DarkBlue);
  /* border-radius: 10px; */

}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}


#emergencyCall:hover {
    background-color: #2576b5;
}

#emergencyCall {
  box-sizing: border-box;
  position: absolute;
  
  margin-left: 38px;
  margin-bottom: 55px;
  bottom: 0;
  left: 0;

  font-family: 'Karla', serif;
  font-weight: bolder;


  text-decoration: none;
  padding: 15px 38px;
  background-color: var(--LightBlue);
  color: #fff;
  border-radius: 18px;
  font-size: 30px;

  border: 8px solid #2576b5;

  text-align: center;
  align-items: center;
  justify-content: center;


  box-shadow: 7px 7px 6px rgba(0, 0, 0, 0.2);
}

```