Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude/settings.local.json.tmp.119157.54e0e4f9c3fa
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(git add *)",
"Bash(git commit -q -m 'Implement API calls, map interactions and solar potential card *)"
]
}
}
14 changes: 4 additions & 10 deletions src/api/getBygningAtPunkt.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { norkartFetch } from './norkartFetch';

export const getBygningAtPunkt = async (x: number, y: number) => {
const apiKey = import.meta.env.VITE_API_KEY;
const query = `https://bygning.api.norkart.no/bygninger/byposition?x=${x}&y=${y}&MaxRadius=1&GeometryTextFormat=GeoJson&IncludeFkbData=true`;

// TODO: Fullfør/endre koden for hente og returnere bygningsdata på et punkt

// Hint: Du kan se på getAdresseFromSearchText og getHoydeFromPunkt for å få en idé om hvordan
// dette kan gjøres.

// Merk at dette er en GET request, og ikke en POST request!

// Når du har fått til kallet til API-et kan du se i Network-taben i nettleseren eller i
// konsollen for å se hvordan responsen ser ut.
const data = await norkartFetch(query);
return data?.Bygninger?.[0];
};
13 changes: 3 additions & 10 deletions src/api/getRosDataForBygning.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { norkartFetch } from './norkartFetch';

export const getRosDataForBygning = async (bygningsNr: number) => {
const apiKey = import.meta.env.VITE_API_KEY;
const query = `https://ros.api.norkart.no/v2/ros/bygning/${bygningsNr}`;

// TODO: Fullfør/endre koden for hente og returnere risiko- og sårbarhetsdata (ROS-data) for en bygning

// Hint: Du kan se på getAdresseFromSearchText og getHoydeFromPunkt for å få en idé om hvordan
// dette kan gjøres.

// Merk at dette er en GET request, og ikke en POST request!

// Når du har fått til kallet til API-et kan du se i Network-taben i nettleseren eller i
// konsollen for å se hvordan responsen ser ut.
return norkartFetch(query);
};
14 changes: 3 additions & 11 deletions src/api/getRuteMellomPunkter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { norkartFetch } from './norkartFetch';

export const getRuteMellomPunkter = async (
startX: number,
startY: number,
stoppX: number,
stoppY: number
) => {
const apiKey = import.meta.env.VITE_API_KEY;
const query = `https://ruteberegner.api.norkart.no/Route/Expanded`;

const postData = {
Expand Down Expand Up @@ -34,14 +35,5 @@ export const getRuteMellomPunkter = async (
ZoomLevel: 14,
};

// TODO: Fullfør/endre koden for å hente og returnere en kjørerute mellom to punkter

// Hint: Du kan se på getHoydeFromPunkt for å få en idé om hvordan dette kan gjøres.
// Dette er en POST request, akkurat som getHoydeFromPunkt.

// Responsen inneholder bl.a. RouteGeometry (en GeoJSON MultiLineString) som kan vises i
// kartet, og en liste med kostnader (CostList) for ruten.

// Når du har fått til kallet til API-et kan du se i Network-taben i nettleseren eller i
// konsollen for å se hvordan responsen ser ut.
return norkartFetch(query, postData);
};
13 changes: 3 additions & 10 deletions src/api/getTakflateDataForBygning.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { norkartFetch } from './norkartFetch';

export const getTakflateDataForBygning = async (bygningsNr: number) => {
const apiKey = import.meta.env.VITE_API_KEY;
const query = `https://takflater.api.norkart.no/takflater/bygning/${bygningsNr}/utvidet`;

// TODO: Fullfør/endre koden for hente og returnere takflatedata for takene på en bygning

// Hint: Du kan se på getAdresseFromSearchText og getHoydeFromPunkt for å få en idé om hvordan
// dette kan gjøres.

// Merk at dette er en GET request, og ikke en POST request!

// Når du har fått til kallet til API-et kan du se i Network-taben i nettleseren eller i
// konsollen for å se hvordan responsen ser ut.
return norkartFetch(query);
};
13 changes: 3 additions & 10 deletions src/api/getTakflateDataForPunkt.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { norkartFetch } from './norkartFetch';

export const getTakflateDataForPunkt = async (x: number, y: number) => {
const apiKey = import.meta.env.VITE_API_KEY;
const query = `https://takflater.api.norkart.no/takflater/punkt/utvidet?x=${x}&y=${y}`;

// TODO: Fullfør/endre koden for hente og returnere takflatedata for et punkt

// Hint: Du kan se på getAdresseFromSearchText og getHoydeFromPunkt for å få en idé om hvordan
// dette kan gjøres.

// Merk at dette er en GET request, og ikke en POST request!

// Når du har fått til kallet til API-et kan du se i Network-taben i nettleseren eller i
// konsollen for å se hvordan responsen ser ut.
return norkartFetch(query);
};
23 changes: 23 additions & 0 deletions src/api/norkartFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const norkartFetch = async (url: string, body?: unknown) => {
const apiKey = import.meta.env.VITE_API_KEY;

try {
const apiResult = await fetch(url, {
method: body ? 'POST' : 'GET',
headers: {
Accept: 'application/json',
...(body ? { 'Content-Type': 'application/json' } : {}),
'X-WAAPI-TOKEN': `${apiKey}`,
},
body: body ? JSON.stringify(body) : undefined,
});

if (apiResult.ok) {
return await apiResult.json();
}
console.error('API request failed with status:', apiResult.status);
} catch (error) {
console.error('An error occurred while fetching data:', error);
}
return undefined;
};
93 changes: 93 additions & 0 deletions src/components/InfoCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Box, Chip, Divider, Typography } from '@mui/material';
import { MONTHS, summarize, type Takflate } from '../solar';

export type PointInfo = {
lng: number;
lat: number;
hoyde?: number;
bygning?: {
Bygningsnummer: number;
MatrikkelData?: { Bygningstype?: string; Bygningstatus?: string };
};
ros?: { Flom?: string; Kvikkleire?: string; AvstandBrannstasjon?: number };
roofs: Takflate[];
};

const nf = new Intl.NumberFormat('nb-NO', { maximumFractionDigits: 0 });

export const InfoCard = ({ info }: { info: PointInfo }) => {
const sol = info.roofs.length ? summarize(info.roofs) : undefined;
const max = sol ? Math.max(...sol.monthlyKwh) : 1;

return (
<Box sx={{ width: 320 }}>
<Typography variant="subtitle2">
{info.lat.toFixed(5)}, {info.lng.toFixed(5)}
{info.hoyde !== undefined && ` · ${info.hoyde.toFixed(1)} moh.`}
</Typography>

{info.bygning && (
<Typography variant="body2" sx={{ mt: 0.5 }}>
{info.bygning.MatrikkelData?.Bygningstype ?? 'Bygning'} (
{info.bygning.MatrikkelData?.Bygningstatus}) · nr.{' '}
{info.bygning.Bygningsnummer}
</Typography>
)}

{info.ros && (
<Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap', mt: 1 }}>
<Chip size="small" label={`Flom: ${info.ros.Flom}`} />
<Chip size="small" label={`Kvikkleire: ${info.ros.Kvikkleire}`} />
<Chip
size="small"
label={`Brannstasjon: ${info.ros.AvstandBrannstasjon} km`}
/>
</Box>
)}

{sol && (
<>
<Divider sx={{ my: 1.5 }} />
<Typography variant="subtitle1" fontWeight="bold">
☀️ Solcelle-orakelet
</Typography>
<Typography variant="body2">
{nf.format(sol.areal)} m² tak kan gi ca.{' '}
<b>{nf.format(sol.yearKwh)} kWh</b> i året.
</Typography>
<Box
sx={{
display: 'flex',
alignItems: 'flex-end',
gap: '3px',
height: 60,
my: 1,
}}
>
{sol.monthlyKwh.map((v, i) => (
<Box
key={MONTHS[i]}
title={`${MONTHS[i]}: ${nf.format(v)} kWh`}
sx={{
flex: 1,
height: `${Math.max((v / max) * 100, 2)}%`,
bgcolor: '#f9a825',
borderRadius: '3px 3px 0 0',
}}
/>
))}
</Box>
<Typography variant="caption" component="div">
Det tilsvarer:
</Typography>
<Typography variant="body2">
🏠 {sol.households.toFixed(1)} norske husholdninger
<br />
🚗 {nf.format(sol.evKm)} km med elbil
<br />☕ {nf.format(sol.coffees)} kopper kaffe
</Typography>
</>
)}
</Box>
);
};
Loading