{t('about.generalDescription.p')}
40 |{t('about.team.p')}
103 | 108 | {t('about.team.link')} 109 | 110 |{t('about.sponsors.p')}
119 |├── .github
├── dependabot.yml
└── workflows
│ ├── build.yml
│ ├── linting.yml
│ └── test-build.yml
├── .gitignore
├── .prettierrc.yml
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── RELEASE-PROCEDURE.md
├── index.html
├── package-lock.json
├── package.json
├── public
├── draco
│ ├── README.md
│ ├── draco_decoder.js
│ ├── draco_decoder.wasm
│ ├── draco_encoder.js
│ ├── draco_wasm_wrapper.js
│ └── gltf
│ │ ├── draco_decoder.js
│ │ ├── draco_decoder.wasm
│ │ ├── draco_encoder.js
│ │ └── draco_wasm_wrapper.js
├── images
│ ├── WelcomeMessage0.png
│ ├── WelcomeMessage1.png
│ ├── WelcomeMessage2.png
│ ├── WelcomeMessage3.png
│ ├── about
│ │ ├── about1.png
│ │ ├── bmbf.jpg
│ │ └── ptf.png
│ └── favicon
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── browserconfig.xml
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon.ico
│ │ ├── mstile-144x144.png
│ │ ├── mstile-150x150.png
│ │ ├── mstile-310x150.png
│ │ ├── mstile-310x310.png
│ │ ├── mstile-70x70.png
│ │ ├── safari-pinned-tab.svg
│ │ └── site.webmanifest
├── locales
│ ├── de
│ │ └── translation.json
│ └── en
│ │ └── translation.json
├── robots.txt
└── sitemap.xml
├── src
├── Main.jsx
├── components
│ ├── ErrorMessages
│ │ └── WrongAdress.jsx
│ ├── Footer.jsx
│ ├── MapPopup.jsx
│ ├── PVSimulation
│ │ ├── SavingsCalculation.jsx
│ │ └── SearchField.jsx
│ ├── Template
│ │ ├── LoadingBar.jsx
│ │ ├── Navigation.jsx
│ │ └── WelcomeMessage.jsx
│ ├── ThreeViewer
│ │ ├── Controls
│ │ │ ├── CustomMapControl.jsx
│ │ │ └── DrawPVControl.jsx
│ │ ├── Meshes
│ │ │ ├── HighlitedPVSystem.jsx
│ │ │ ├── HiglightedMesh.jsx
│ │ │ ├── PVSystems.jsx
│ │ │ ├── SimulationMesh.jsx
│ │ │ ├── SurroundingMesh.jsx
│ │ │ └── VegetationMesh.jsx
│ │ ├── Overlay.jsx
│ │ ├── PointsAndEdges.jsx
│ │ ├── Scene.jsx
│ │ ├── Terrain.jsx
│ │ └── TextSprite.jsx
│ └── ui
│ │ ├── accordion.jsx
│ │ ├── button.jsx
│ │ ├── close-button.jsx
│ │ ├── color-mode.jsx
│ │ ├── data-list.jsx
│ │ ├── dialog.jsx
│ │ ├── field.jsx
│ │ ├── number-input.jsx
│ │ ├── progress.jsx
│ │ ├── provider.jsx
│ │ ├── slider.jsx
│ │ ├── switch.jsx
│ │ └── toaster.jsx
├── data
│ ├── constants.js
│ └── dataLicense.js
├── i18n.js
├── index.jsx
├── pages
│ ├── About.jsx
│ ├── Datenschutz.jsx
│ ├── Impressum.jsx
│ ├── Map.jsx
│ ├── NotFound.jsx
│ └── Simulation.jsx
├── simulation
│ ├── download.js
│ ├── elevation.js
│ ├── location.js
│ ├── main.js
│ ├── preprocessing.js
│ └── processVegetationTiffs.js
└── static
│ └── css
│ └── main.css
├── update.sh
└── vite.config.js
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: 'npm'
9 | directory: '/' # Location of package manifest
10 | schedule:
11 | interval: 'weekly'
12 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: 👷♀️ Build website to deployment branch
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | types: [published]
7 |
8 | jobs:
9 | deployment:
10 | runs-on: ubuntu-latest
11 | environment: production
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 | with:
16 | persist-credentials: false
17 | - name: Setup Node
18 | uses: actions/setup-node@v3
19 | - name: Install
20 | run: npm ci
21 | - name: Build and Deploy
22 | env:
23 | NODE_ENV: production
24 | # This is set automatically by github
25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 | run: |
27 | git config user.name "Automated"
28 | git config user.email "actions@users.noreply.github.com"
29 | git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/open-pv/website.git
30 | npm run build
31 | npm run deploy
32 |
--------------------------------------------------------------------------------
/.github/workflows/linting.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | run-linters:
10 | if: github.event.pull_request.draft == false
11 | name: Run linters
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: Check out Git repository
16 | uses: actions/checkout@v4
17 |
18 | - name: Set up Node.js
19 | uses: actions/setup-node@v4
20 | with:
21 | node-version: 20
22 |
23 | - name: Install dependencies
24 | run: npm ci
25 |
26 | - name: Run linters
27 | uses: wearerequired/lint-action@v2
28 | with:
29 | eslint: false
30 | prettier: true
31 |
--------------------------------------------------------------------------------
/.github/workflows/test-build.yml:
--------------------------------------------------------------------------------
1 | name: 👷 Deploy test site to github pages
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | deployment:
11 | runs-on: ubuntu-latest
12 | environment: production
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 | with:
17 | persist-credentials: false
18 | - name: Setup Node
19 | uses: actions/setup-node@v3
20 | - name: Install
21 | run: |
22 | npm ci
23 | npm install @rollup/rollup-linux-x64-gnu
24 | - name: Build and Deploy
25 | env:
26 | NODE_ENV: production
27 | # This is set automatically by github
28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 | run: |
30 | git config user.name "Automated"
31 | git config user.email "actions@users.noreply.github.com"
32 | git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/open-pv/website.git
33 | echo "User-agent: *" > public/robots.txt
34 | echo "Disallow: /" >> public/robots.txt
35 | echo "test.openpv.de" > public/CNAME
36 | npm run build
37 | npm run deploy:test
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # prefer npm over yarn
2 | yarn.lock
3 |
4 | # eslint
5 | .eslintcache
6 |
7 | # Logs
8 | logs
9 | *.log
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 |
16 | # Enviromental
17 | .env
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # Compiled binary addons (http://nodejs.org/api/addons.html)
29 | build/Release
30 |
31 | # Dependency directory
32 | # Commenting this out is preferred by some people, see
33 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
34 | node_modules
35 |
36 | # Users Environment Variables
37 | .lock-wscript
38 |
39 | # Webpack related
40 | public/dist/
41 | dist/
42 | tmp/
43 | build/
44 |
45 | # OSX
46 | .DS_Store
47 |
48 | # Nohup
49 | nohup.out
50 |
--------------------------------------------------------------------------------
/.prettierrc.yml:
--------------------------------------------------------------------------------
1 | semi: false
2 | singleQuote: true
3 | jsxSingleQuote: true
4 | endOfLine: 'lf'
5 | trailingComma: 'all'
6 | tabWidth: 2
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[css]": {
3 | "editor.suggest.insertMode": "replace"
4 | },
5 | "editor.formatOnSave": true,
6 | "editor.defaultFormatter": "esbenp.prettier-vscode"
7 | }
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.openpv.de/)
2 |
3 | # The OpenPV website
4 |
5 | This is the base repository for the website [openpv.de](https://www.openpv.de). The website is built using
6 |
7 | - [React](https://react.dev/)
8 | - [Chakra-UI](https://v2.chakra-ui.com)
9 | - [Three.js](https://threejs.org/)
10 |
11 | The whole site is **static**, reducing the hosting costs as much as possible. The shading simulation happens in the browser, using
12 | our npm package [simshady](https://github.com/open-pv/simshady).
13 |
14 | ## Setup
15 |
16 | If you want to deploy this website locally, you need to follow these steps:
17 |
18 | 1. Clone the repository and enter it.
19 | 2. Make sure that you have [node](https://nodejs.org/en) and the node package manager npm installed. Check this by running
20 | ```
21 | node --version
22 | npm --version
23 | ```
24 | 3. Install all required packages from `package.json` by running
25 | ```shell
26 | npm install
27 | ```
28 | 4. To build the code and host it in a development environment, run
29 | ```shell
30 | npm run dev
31 | ```
32 | and visit [localhost:5173](http://localhost:5173).
33 |
34 | ## How does this work?
35 |
36 | We have a detailed description in german and english on our [About Page](https://www.openpv.de/about). Also check out our [blog](https://blog.openpv.de).
37 |
38 | ## Funding
39 |
40 | We thank our sponsors.
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/RELEASE-PROCEDURE.md:
--------------------------------------------------------------------------------
1 | # Release Procedure
2 |
3 | ## Version Numbers
4 |
5 | This software follows the [Semantic Versioning (SemVer)](https://semver.org/).
6 | It always has the format `MAJOR.MINOR.PATCH`, e.g. `1.5.0`.
7 |
8 | ## GitHub Release
9 |
10 | ### 1. 📝 Check correctness of test.openpv.de
11 |
12 | - Navigate to test.openpv.de
13 | - Check that this is the website you want to deploy
14 | - Check that it has no bugs
15 |
16 | ### 2. 🐙 Create a `GitHub Release`
17 |
18 | - Named `v0.12.1`
19 | - Possibly add a Title to the Release Notes Headline
20 | - Summarize key changes in the description
21 | - Use the `generate release notes` button provided by GitHub
22 | - Make sure that new contributors are mentioned
23 | - Choose the correct git `tag`
24 | - Choose the `main` branch
25 | - Publish release
26 |
27 | ### 3. Deployment
28 |
29 | - Start the manual deployment process based on the `build` branch
30 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{children}
43 |74 | Basiskarte ©{' '} 75 | 80 | BKG 81 | 82 | ( 83 | 88 | dl-de/by-2-0 89 | 90 | ) | Geländemodell: 91 | 96 | © Sonny 97 | 98 | ( 99 | 104 | CC-BY-4.0 105 | 106 | ), erstellt aus 107 | 112 | verschiedenen Quellen 113 | 114 |
115 | )} 116 | {federalState && ( 117 | <> 118 |123 | Gebäudedaten ©{' '} 124 | 125 | {attr.attribution} 126 | 127 | ( 128 | 133 | {attr.license} 134 | 135 | ) 136 |
137 | > 138 | )} 139 |140 | © 141 | 146 | Team OpenPV 147 | 148 | {' | '} 149 | Impressum 150 | {' | '} 151 | {t('Footer.privacyPolicy')} 152 | {' | '} 153 | { 156 | e.preventDefault() 157 | changeLanguage('en') 158 | }} 159 | > 160 | English 161 | 162 | {' | '} 163 | { 166 | e.preventDefault() 167 | changeLanguage('de') 168 | }} 169 | > 170 | German 171 | 172 |
173 | > 174 | ) 175 | 176 | return27 | {t('loadingMessage.tip' + shownTip.toString())} 28 |
29 |{t(`WelcomeMessage.introduction`)}
53 |{t('about.generalDescription.p')}
40 |{t('about.team.p')}
103 | 108 | {t('about.team.link')} 109 | 110 |{t('about.sponsors.p')}
119 |15 | Verantwortlicher im Sinne der Datenschutzgesetze, insbesondere der 16 | EU-Datenschutzgrundverordnung (DSGVO), ist: 17 |
18 |
19 | Großhauser Heidler Kotthoff Pöppel GbR
p r i v a c y @ o p e n
20 | p v . d e
21 |
24 | Unter den angegebenen Kontaktdaten unseres Datenschutzbeauftragten 25 | können Sie jederzeit folgende Rechte ausüben: 26 |
27 |52 | Sofern Sie uns eine Einwilligung erteilt haben, können Sie diese 53 | jederzeit mit Wirkung für die Zukunft widerrufen. 54 |
55 |56 | Sie können sich jederzeit mit einer Beschwerde an eine 57 | Aufsichtsbehörde wenden, z. B. an die zuständige Aufsichtsbehörde 58 | des Bundeslands Ihres Wohnsitzes oder an die für uns als 59 | verantwortliche Stelle zuständige Behörde. 60 |
61 |62 | Eine Liste der Aufsichtsbehörden (für den nichtöffentlichen Bereich) 63 | mit Anschrift finden Sie unter:{' '} 64 | 69 | https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html 70 | 71 | . 72 |
73 | 74 |118 | Wir verwenden Ihre Daten nicht, um Rückschlüsse auf Ihre Person zu 119 | ziehen. 120 |
121 |123 | Die Verarbeitung erfolgt gemäß Art. 6 Abs. 1 lit. f DSGVO auf Basis 124 | unseres berechtigten Interesses an der Verbesserung der Stabilität 125 | und Funktionalität unserer Website. 126 |
127 |129 | Empfänger der Daten sind technische Dienstleister, die den Betrieb 130 | unserer Server ermöglichen. Diese Dienstleister sind die netcup GmbH 131 | und Hetzner Online GmbH. 132 |
133 |135 | Unsere eigene Datenverarbeitung findet in Deutschland statt. Für die 136 | Konvertierung von Adresse in Koordinaten werden Daten über Nominatim 137 | in Drittländern (Stand August 2024: Niederlande und UK) verarbeitet. 138 | Eine Auflistung dieser Drittländer finden Sie hier: 139 | https://osmfoundation.org/wiki/Privacy_Policy#Where_do_we_store_the_data 140 |
141 | 142 |144 | Die Daten werden gelöscht, sobald diese für den Zweck der Erhebung 145 | nicht mehr erforderlich sind. Dies ist für die Daten, die der 146 | Bereitstellung der Website dienen, grundsätzlich der Fall, wenn die 147 | jeweilige Sitzung beendet ist. 148 |
149 |150 | Im Falle der Speicherung der Verkehrsdaten in Logfiles ist dies nach 151 | 14 Tagen der Fall, es sei denn, es gibt einen besonderen Grund, 152 | Informationen länger aufzubewahren (z.B. wenn einzelne IP-Adressen 153 | gesperrt werden). 154 |
155 | 156 |
158 | Nominatim Geocoding
159 | Diese Seite nutzt über eine API Nominatim, den Geocoding Dienst von
160 | OpenStreetMaps.nominatim.openstreetmap.org Zur Nutzung der
161 | GeoCodingFunktion bei der Umwandlung ihrer Adresse in Koordinaten
162 | werden die eingegebenen Adressdaten an Nominatim zur
163 | Koordinatenermittlung übermittelt. Die Nutzung von Nominatim erfolgt
164 | im Interesse einer ansprechenden Darstellung unserer Online-Angebote
165 | und an einer leichten Auffindbarkeit der von uns simulierten
166 | PV-Erträge auf der Website. Dies stellt ein berechtigtes Interesse
167 | im Sinne von Art. 6 Abs. 1 lit. f DSGVO dar. Mehr Informationen zum
168 | Umgang mit Nutzerdaten finden Sie in der Datenschutzerklärung von
169 | OpenStreetMaps: https://wiki.osmfoundation.org/wiki/Privacy_Policy
170 |
173 | Photon von Komoot
174 | openpv nutzt die API von komoot zur automatischen
175 | Adressvervollständigung. Dabei werden die eingegebenen Buchstaben an
176 | https://photon.komoot.io/ gesendet. Die Auswahl einer finalen
177 | Adresse wird nicht an komoot mitgeteilt.
178 |
183 | Sie haben das Recht, aus Gründen, die sich aus Ihrer besonderen 184 | Situation ergeben, jederzeit gegen die Verarbeitung Sie betreffender 185 | personenbezogener Daten, die aufgrund Art. 6 Abs. 1 lit. f DSGVO 186 | (Datenverarbeitung auf der Grundlage einer Interessenabwägung) 187 | erfolgt, Widerspruch einzulegen. 188 |
189 |190 | Legen Sie Widerspruch ein, werden wir Ihre personenbezogenen Daten 191 | nicht mehr verarbeiten, es sei denn, wir können zwingende 192 | schutzwürdige Gründe für die Verarbeitung nachweisen, die Ihre 193 | Interessen, Rechte und Freiheiten überwiegen, oder die Verarbeitung 194 | dient der Geltendmachung, Ausübung oder Verteidigung von 195 | Rechtsansprüchen. 196 |
197 |200 | Wir behalten uns vor, diese Datenschutzerklärung anzupassen, damit 201 | sie stets den aktuellen rechtlichen Anforderungen entspricht oder um 202 | Änderungen unserer Leistungen in der Datenschutzerklärung 203 | umzusetzen, z.B. bei der Einführung neuer Services. Für Ihren 204 | erneuten Besuch gilt dann die neue Datenschutzerklärung. 205 |
206 |208 | Wenn Sie Fragen zum Datenschutz haben, schreiben Sie uns bitte eine 209 | E-Mail oder wenden Sie sich direkt an die für den Datenschutz 210 | verantwortliche Person in unserer Organisation: 211 |
212 |213 | 214 | Die Datenschutzerklärung wurde mithilfe der activeMind AG 215 | erstellt, den Experten für{' '} 216 | 221 | externe Datenschutzbeauftragte 222 | {' '} 223 | (Version #2020-09-30). 224 | 225 |
226 |
15 | Martin Großhauser
16 | Arnulfstraße 138
17 | 80634 München
18 | info@openpv.de
19 |
22 | Alle Inhalte unseres Internetauftritts wurden mit größter Sorgfalt 23 | und nach bestem Gewissen erstellt. Für die Richtigkeit, 24 | Vollständigkeit und Aktualität der Inhalte können wir jedoch keine 25 | Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG 26 | für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen 27 | verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter 28 | jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde 29 | Informationen zu überwachen oder nach Umständen zu forschen, die auf 30 | eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur 31 | Entfernung oder Sperrung der Nutzung von Informationen nach den 32 | allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche 33 | Haftung ist jedoch erst ab dem Zeitpunkt der Kenntniserlangung einer 34 | konkreten Rechtsverletzung möglich. Bei Bekanntwerden von den o.g. 35 | Rechtsverletzungen werden wir diese Inhalte unverzüglich entfernen. 36 |
37 |39 | Unsere Webseite enthält Links auf externe Webseiten Dritter. Auf die 40 | Inhalte dieser direkt oder indirekt verlinkten Webseiten haben wir 41 | keinen Einfluss. Daher können wir für die „externen Links“ auch 42 | keine Gewähr auf Richtigkeit der Inhalte übernehmen. Für die Inhalte 43 | der externen Links sind die jeweilige Anbieter oder Betreiber 44 | (Urheber) der Seiten verantwortlich. Die externen Links wurden zum 45 | Zeitpunkt der Linksetzung auf eventuelle Rechtsverstöße überprüft 46 | und waren im Zeitpunkt der Linksetzung frei von rechtswidrigen 47 | Inhalten. Eine ständige inhaltliche Überprüfung der externen Links 48 | ist ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht 49 | möglich. Bei direkten oder indirekten Verlinkungen auf die Webseiten 50 | Dritter, die außerhalb unseres Verantwortungsbereichs liegen, würde 51 | eine Haftungsverpflichtung ausschließlich in dem Fall nur bestehen, 52 | wenn wir von den Inhalten Kenntnis erlangen und es uns technisch 53 | möglich und zumutbar wäre, die Nutzung im Falle rechtswidriger 54 | Inhalte zu verhindern. Diese Haftungsausschlusserklärung gilt auch 55 | innerhalb des eigenen Internetauftrittes „Name Ihrer Domain“ 56 | gesetzten Links und Verweise von Fragestellern, Blogeinträgern, 57 | Gästen des Diskussionsforums. Für illegale, fehlerhafte oder 58 | unvollständige Inhalte und insbesondere für Schäden, die aus der 59 | Nutzung oder Nichtnutzung solcherart dargestellten Informationen 60 | entstehen, haftet allein der Diensteanbieter der Seite, auf welche 61 | verwiesen wurde, nicht derjenige, der über Links auf die jeweilige 62 | Veröffentlichung lediglich verweist. Werden uns Rechtsverletzungen 63 | bekannt, werden die externen Links durch uns unverzüglich entfernt. 64 |
65 |67 | Die auf unserer Webseite veröffentlichen Inhalte und Werke 68 | unterliegen dem deutschen Urheberrecht 69 | (http://www.gesetze-im-internet.de/bundesrecht/urhg/gesamt.pdf) . 70 | Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der 71 | Verwertung des geistigen Eigentums in ideeller und materieller Sicht 72 | des Urhebers außerhalb der Grenzen des Urheberrechtes bedürfen der 73 | vorherigen schriftlichen Zustimmung des jeweiligen Urhebers i.S.d. 74 | Urhebergesetzes 75 | (http://www.gesetze-im-internet.de/bundesrecht/urhg/gesamt.pdf ). 76 | Downloads und Kopien dieser Seite sind nur für den privaten und 77 | nicht kommerziellen Gebrauch erlaubt. Sind die Inhalte auf unserer 78 | Webseite nicht von uns erstellt wurden, sind die Urheberrechte 79 | Dritter zu beachten. Die Inhalte Dritter werden als solche kenntlich 80 | gemacht. Sollten Sie trotzdem auf eine Urheberrechtsverletzung 81 | aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei 82 | Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte 83 | unverzüglich entfernen. 84 |
85 |16 | Return home. 17 |
18 |