Skip to content

Commit

Permalink
adds a map-viewer widget based on the network-map-viewer component (f…
Browse files Browse the repository at this point in the history
…rom powsybl-diagram-viewer)

Signed-off-by: Christian Biasuzzi <[email protected]>
  • Loading branch information
CBiasuzzi committed Jun 6, 2024
1 parent 6ffb715 commit 24a733a
Show file tree
Hide file tree
Showing 9 changed files with 566 additions and 113 deletions.
Binary file not shown.
59 changes: 59 additions & 0 deletions examples/data/demo_geo1.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_12" xmlns:lp="http://www.powsybl.org/schema/iidm/ext/line_position/1_0" xmlns:sp="http://www.powsybl.org/schema/iidm/ext/substation_position/1_0" id="sim1" caseDate="2022-04-06T13:43:05.020+02:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
<iidm:substation id="P1" country="FR" tso="RTE" geographicalTags="A">
<iidm:voltageLevel id="VLGEN" nominalV="24.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="NGEN"/>
</iidm:busBreakerTopology>
<iidm:generator id="GEN" energySource="OTHER" minP="-9999.99" maxP="9999.99" voltageRegulatorOn="true" targetP="607.0" targetV="24.5" targetQ="301.0" bus="NGEN" connectableBus="NGEN">
<iidm:minMaxReactiveLimits minQ="-9999.99" maxQ="9999.99"/>
</iidm:generator>
</iidm:voltageLevel>
<iidm:voltageLevel id="VLHV1" nominalV="380.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="NHV1"/>
</iidm:busBreakerTopology>
</iidm:voltageLevel>
<iidm:twoWindingsTransformer id="NGEN_NHV1" r="0.26658461538461536" x="11.104492831516762" g="0.0" b="0.0" ratedU1="24.0" ratedU2="400.0" bus1="NGEN" connectableBus1="NGEN" voltageLevelId1="VLGEN" bus2="NHV1" connectableBus2="NHV1" voltageLevelId2="VLHV1"/>
</iidm:substation>
<iidm:substation id="P2" country="FR" tso="RTE" geographicalTags="B">
<iidm:voltageLevel id="VLHV2" nominalV="380.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="NHV2"/>
</iidm:busBreakerTopology>
</iidm:voltageLevel>
<iidm:voltageLevel id="VLLOAD" nominalV="150.0" topologyKind="BUS_BREAKER">
<iidm:busBreakerTopology>
<iidm:bus id="NLOAD"/>
</iidm:busBreakerTopology>
<iidm:load id="LOAD" loadType="UNDEFINED" p0="600.0" q0="200.0" bus="NLOAD" connectableBus="NLOAD"/>
</iidm:voltageLevel>
<iidm:twoWindingsTransformer id="NHV2_NLOAD" r="0.04724999999999999" x="4.049724365620455" g="0.0" b="0.0" ratedU1="400.0" ratedU2="158.0" bus1="NHV2" connectableBus1="NHV2" voltageLevelId1="VLHV2" bus2="NLOAD" connectableBus2="NLOAD" voltageLevelId2="VLLOAD">
<iidm:ratioTapChanger lowTapPosition="0" tapPosition="1" targetDeadband="0.0" loadTapChangingCapabilities="true" regulating="true" regulationMode="VOLTAGE" regulationValue="158.0">
<iidm:terminalRef id="NHV2_NLOAD" side="TWO"/>
<iidm:step r="0.0" x="0.0" g="0.0" b="0.0" rho="0.8505666905244191"/>
<iidm:step r="0.0" x="0.0" g="0.0" b="0.0" rho="1.0006666666666666"/>
<iidm:step r="0.0" x="0.0" g="0.0" b="0.0" rho="1.150766642808914"/>
</iidm:ratioTapChanger>
</iidm:twoWindingsTransformer>
</iidm:substation>
<iidm:line id="NHV1_NHV2_1" r="3.0" x="33.0" g1="0.0" b1="1.93E-4" g2="0.0" b2="1.93E-4" bus1="NHV1" connectableBus1="NHV1" voltageLevelId1="VLHV1" bus2="NHV2" connectableBus2="NHV2" voltageLevelId2="VLHV2"/>
<iidm:line id="NHV1_NHV2_2" r="3.0" x="33.0" g1="0.0" b1="1.93E-4" g2="0.0" b2="1.93E-4" bus1="NHV1" connectableBus1="NHV1" voltageLevelId1="VLHV1" bus2="NHV2" connectableBus2="NHV2" voltageLevelId2="VLHV2"/>
<iidm:extension id="P1">
<sp:substationPosition>
<sp:coordinate longitude="2.0" latitude="48.0"/>
</sp:substationPosition>
</iidm:extension>
<iidm:extension id="P2">
<sp:substationPosition>
<sp:coordinate longitude="2.1" latitude="48.1"/>
</sp:substationPosition>
</iidm:extension>

<iidm:extension id="NHV1_NHV2_1">
<lp:linePosition>
<lp:coordinate longitude="2.0" latitude="48.0"/>
<lp:coordinate longitude="2.1" latitude="48.1"/>
</lp:linePosition>
</iidm:extension>
</iidm:network>
137 changes: 137 additions & 0 deletions examples/demo_mapviewer.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pypowsybl.network as pn\n",
"import pypowsybl.loadflow as lf"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pypowsybl_jupyter import NetworkMapWidget"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Example 1\n",
"# load a CGMES file containing a GL profile (Graphical Layout) and display it with the NetworkMap widget"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"network2 = pn.load('./data/MicroGridTestConfiguration_T4_BE_BB_Complete_v2.zip', {'iidm.import.cgmes.post-processors': 'cgmesGLImport'})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"network2_lf_results=lf.run_ac(network2, parameters=lf.Parameters(distributed_slack=False))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"NetworkMapWidget(network2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Example 2\n",
"# load a network that includes geographical data extensions and display it with the NetworkMap widget"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"network=pn.load('./data/demo_geo1.xml')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"lf_results=lf.run_ac(network, parameters=lf.Parameters(distributed_slack=False))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"network_map=NetworkMapWidget(network)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(network_map)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# center the map on a specific substation\n",
"network_map.center_on_substation('P1')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
8 changes: 8 additions & 0 deletions js/networkmapwidget.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.network-map-viewer-widget {
border: 2px solid lightgrey;
display: inline-block;
}

.maplibregl-ctrl-bottom-left {
display: none;
}
169 changes: 169 additions & 0 deletions js/networkmapwidget.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright (c) 2024, RTE (http://www.rte-france.com)
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-License-Identifier: MPL-2.0
//

import React, { useEffect, useRef, useState } from 'react';

import { createRender, useModelState } from '@anywidget/react';
import { NetworkMap, GeoData, MapEquipments } from '@powsybl/diagram-viewer';
import './networkmapwidget.css';

import {
createTheme,
ThemeProvider,
StyledEngineProvider,
} from '@mui/material/styles';

const INITIAL_ZOOM = 9;
const LABELS_ZOOM_THRESHOLD = 9;
const ARROWS_ZOOM_THRESHOLD = 7;
const useName = true;

const darkTheme = createTheme({
palette: {
mode: 'dark',
},
link: {
color: 'green',
},
node: {
background: '#1976d2',
hover: '#90caf9',
border: '#cce3f9',
},
selectedRow: {
background: '#545C5B',
},
mapboxStyle: 'mapbox://styles/mapbox/dark-v9',
aggrid: 'ag-theme-alpine-dark',
});

class WidgetMapEquipments extends MapEquipments {
initEquipments(smapdata, lmapdata) {
this.updateSubstations(smapdata, true);
this.updateLines(lmapdata, true);
}

constructor(smapdata, lmapdata) {
super();
this.initEquipments(smapdata, lmapdata);
}
}

//called after a click (right mouse click) on an equipment (line or substation)
function showEquipmentMenu(equipment, x, y, type) {
console.log(
'# Show equipment menu: ' +
JSON.stringify(equipment) +
', type: ' +
type
);
}

const render = createRender(() => {
const networkMapRef = useRef();

const [spos] = useModelState('spos');
const [lpos] = useModelState('lpos');
const [smap] = useModelState('smap');
const [lmap] = useModelState('lmap');

const [params, setParams] = useModelState('params');
const targetSubId = params['subId'];
const [centerOnSubId, setCenterOnSubId] = useState(
targetSubId === null ? null : { to: targetSubId }
);

const geoData = new GeoData(new Map(), new Map());
geoData.setSubstationPositions(JSON.parse(spos));
geoData.setLinePositions(JSON.parse(lpos));

const mapEquipments = new WidgetMapEquipments(
JSON.parse(smap),
JSON.parse(lmap)
);

useEffect(() => {
const handleContextmenu = (e) => {
//e.preventDefault();
e.stopPropagation();
};
networkMapRef.current.addEventListener(
'contextmenu',
handleContextmenu
);
return () => {
networkMapRef.current.removeEventListener(
'contextmenu',
handleContextmenu
);
};
}, []);

useEffect(() => {
const targetSubId = params['subId'];
if (!('centered' in params)) {
setCenterOnSubId(targetSubId === null ? null : { to: targetSubId });
setParams({ ...params, centered: true });
}
}, [params]);

return (
<div ref={networkMapRef} className="network-map-viewer-widget">
<StyledEngineProvider injectFirst>
<ThemeProvider theme={darkTheme}>
<div
style={{
position: 'relative',
width: 800,
height: 600,
}}
>
<NetworkMap
ref={networkMapRef}
mapEquipments={mapEquipments}
geoData={geoData}
labelsZoomThreshold={LABELS_ZOOM_THRESHOLD}
arrowsZoomThreshold={ARROWS_ZOOM_THRESHOLD}
initialZoom={INITIAL_ZOOM}
useName={useName}
centerOnSubstation={centerOnSubId}
onSubstationClick={(vlId) => {
console.log('# OpenVoltageLevel: ' + vlId);
}}
onSubstationClickChooseVoltageLevel={(
idSubstation,
x,
y
) =>
console.log(
`# Choose Voltage Level for substation: ${idSubstation} at coordinates (${x}, ${y})`
)
}
onSubstationMenuClick={(equipment, x, y) =>
showEquipmentMenu(equipment, x, y, 'substation')
}
onLineMenuClick={(equipment, x, y) =>
showEquipmentMenu(equipment, x, y, 'line')
}
onVoltageLevelMenuClick={(equipment, x, y) => {
console.log(
`# VoltageLevel menu click: ${JSON.stringify(
equipment
)} at coordinates (${x}, ${y})`
);
}}
mapLibrary={'cartonolabel'}
mapTheme={'dark'}
/>
</div>
</ThemeProvider>
</StyledEngineProvider>
</div>
);
});

export default { render };
Loading

0 comments on commit 24a733a

Please sign in to comment.