Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIG-1499 add map #684

Merged
merged 14 commits into from Mar 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
873 changes: 773 additions & 100 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -55,11 +55,13 @@
],
"dependencies": {
"@babel/polyfill": "^7.8.7",
"@datapunt/amsterdam-react-maps": "git+https://github.com/Amsterdam/amsterdam-react-maps.git#feature/package-export",
aditudorache marked this conversation as resolved.
Show resolved Hide resolved
"@datapunt/asc-assets": "^0.18.1",
"@datapunt/asc-core": "^0.18.1",
"@datapunt/asc-ui": "^0.18.2",
"@datapunt/leaflet-geojson-bbox-layer": "0.1.1",
"@datapunt/matomo-tracker-js": "0.0.15",
"@datapunt/react-maps": "^0.7.2",
"@sentry/browser": "^5.14.2",
"abortcontroller-polyfill": "^1.4.0",
"amsterdam-amaps": "^2.0.0",
Expand Down
36 changes: 36 additions & 0 deletions src/components/MapEditor/__tests__/MapEditor.test.js
@@ -0,0 +1,36 @@
import React from 'react';
import { render } from '@testing-library/react';

import MAP_OPTIONS from 'shared/services/configuration/map-options';
import Map from '..';

describe('components/MapEditor', () => {
const testLocation = {
geometrie: {
type: 'Point',
coordinates: [4, 52],
},
};

it('should render the map', () => {
const { container, getByTestId, queryByText } = render(<Map options={MAP_OPTIONS} location={{}}/>);

// Map
expect(getByTestId('map-test-id')).toBeInTheDocument();

// Tile layer
expect(queryByText(/Kaartgegevens CC-BY-4.0 Gemeente Amsterdam/)).toBeInTheDocument();

// Zoom
expect(container.querySelector('button[title="Inzoomen"]')).toBeInTheDocument();
});

it('should render the marker', () => {
const { container, rerender } = render(<Map options={MAP_OPTIONS} location={{}}/>);

expect(container.querySelector('.sia-map-marker')).not.toBeInTheDocument();

rerender(<Map options={MAP_OPTIONS} location={testLocation} />);
expect(container.querySelector('.sia-map-marker')).toBeInTheDocument();
});
});
81 changes: 81 additions & 0 deletions src/components/MapEditor/index.js
@@ -0,0 +1,81 @@
import React, { memo, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Map as MapComponent, Marker, TileLayer } from '@datapunt/react-maps';
import { ViewerContainer } from '@datapunt/asc-ui';
import { Zoom } from '@datapunt/amsterdam-react-maps/lib/components';
import styled from '@datapunt/asc-core';
import { markerIcon } from 'shared/services/configuration/map-markers';
import { feature2location } from 'shared/services/map-location';

const MapWrapper = styled.div`
position: relative;
`;

const StyledViewerContainer = styled(ViewerContainer)`
z-index: 400; // this elevation ensures that this container comes on top of the internal leaflet components
`;

const Map = ({ location, options, ...otherProps }) => {
const [marker, setMarker] = useState();

useEffect(() => {
if (!marker || !location?.geometrie) return;
const opacity = 1;
const latlng = feature2location(location.geometrie);
aditudorache marked this conversation as resolved.
Show resolved Hide resolved
marker.setLatLng(latlng);
marker.setOpacity(opacity);
}, [marker, location]);

return (
<MapWrapper>
<MapComponent data-testid="map-test-id" options={options} {...otherProps}>
<StyledViewerContainer bottomRight={<Zoom />} />
{location && location.geometrie && (
<Marker
setInstance={setMarker}
args={[
{
lat: 0,
lng: 0,
},
]}
options={{
icon: markerIcon,
opacity: 0,
}}
/>
)}

<TileLayer
args={['https://{s}.data.amsterdam.nl/topo_rd/{z}/{x}/{y}.png']}
options={{
subdomains: ['t1', 't2', 't3', 't4'],
tms: true,
attribution: 'Kaartgegevens CC-BY-4.0 Gemeente Amsterdam',
}}
/>
</MapComponent>
</MapWrapper>
);
};

Map.propTypes = {
location: PropTypes.shape({
geometrie: PropTypes.shape({
type: PropTypes.string,
coordinates: PropTypes.arrayOf(PropTypes.number).isRequired,
}),
address: PropTypes.shape({
openbare_ruimte: PropTypes.string,
huisnummer: PropTypes.string,
huisletter: PropTypes.string,
huisnummer_toevoeging: PropTypes.string,
postcode: PropTypes.string,
woonplaats: PropTypes.string,
}),
}).isRequired,
options: PropTypes.shape({})
.isRequired /** leaflet options, See `https://leafletjs.com/reference-1.6.0.html#map-option` */,
};

export default memo(Map);
1 change: 1 addition & 0 deletions src/components/SiteHeader/index.js
Expand Up @@ -5,6 +5,7 @@ import styled, { css } from 'styled-components';
import Media from 'react-media';

import { svg, Logout as LogoutIcon } from '@datapunt/asc-assets';

import {
Header as HeaderComponent,
MenuFlyOut,
Expand Down
16 changes: 16 additions & 0 deletions src/shared/services/configuration/map-markers.js
@@ -0,0 +1,16 @@
/* eslint-disable global-require */
import L from 'leaflet';

export const smallMarkerIcon = L.icon({
iconUrl: 'https://map.data.amsterdam.nl/dist/images/svg/marker.svg',
iconSize: [20, 20],
iconAnchor: [10, 19],
className: 'sia-marker-small',
});

export const markerIcon = L.icon({
iconUrl: 'https://map.data.amsterdam.nl/dist/images/svg/marker.svg',
iconSize: [40, 40],
iconAnchor: [20, 39],
className: 'sia-map-marker',
});
20 changes: 20 additions & 0 deletions src/shared/services/configuration/map-options.js
@@ -0,0 +1,20 @@
import { getCrsRd } from '@datapunt/amsterdam-react-maps/lib/utils';

export const DEFAULT_MARKER_POSITION = {
aditudorache marked this conversation as resolved.
Show resolved Hide resolved
lat: 52.3731081,
lng: 4.8932945,
};


const MAP_OPTIONS = {
center: [52.3731081, 4.8932945],
zoomControl: false,
zoom: 10,
crs: getCrsRd(),
maxBounds: [
[52.25168, 4.64034],
[52.50536, 5.10737],
],
};

export default MAP_OPTIONS;
61 changes: 44 additions & 17 deletions src/shared/services/map-location/index.js
@@ -1,28 +1,55 @@
function mapLocation(loc) {
export const feature2location = feature => {
const { coordinates } = feature;
return {
lat: coordinates[1],
lng: coordinates[0],
};
};

export const address2pdok = address => {
aditudorache marked this conversation as resolved.
Show resolved Hide resolved
const { openbare_ruimte, huisnummer, huisletter, huisnummer_toevoeging, postcode, woonplaats } = address;

return {
straatnaam: openbare_ruimte,
aditudorache marked this conversation as resolved.
Show resolved Hide resolved
huisnummer: `${huisnummer}`,
huisletter: `${huisletter}` || '',
huisnummertoevoeging: huisnummer_toevoeging ? `${huisnummer_toevoeging}` : '',
postcode,
woonplaatsnaam: woonplaats,
};
};

/**
* converts the location from `sia` location format to latlon format
*/
const mapLocation = loc => {
const location = {};

if (loc.dichtstbijzijnd_adres) {
location.address = { ...loc.dichtstbijzijnd_adres };
location.address.huisnummer = `${location.address.huisnummer}`;
location.address.huisnummer_toevoeging = `${location.address.huisnummer_toevoeging}`;
if (loc.geometrie) {
location.location = feature2location(loc.geometrie);
}

if (loc.omgevingsinfo) {
location.buurt_code = loc.omgevingsinfo.buurtcode;
location.stadsdeel = loc.omgevingsinfo.stadsdeelcode;
if (loc.buurt_code) {
location.buurtcode = loc.buurt_code;
}

if (loc.query) {
location.geometrie = {
type: 'Point',
coordinates: [
loc.query.longitude,
loc.query.latitude,
],
};
if (loc.stadsdeel) {
location.stadsdeelcode = loc.stadsdeel;
}

if (loc.address) {
location.address = address2pdok(loc.address);
}

return location;
}
};

export const formatAddress = address => {
aditudorache marked this conversation as resolved.
Show resolved Hide resolved
const toevoeging = address.huisnummer_toevoeging ? `-${address.huisnummer_toevoeging}` : '';
const display = address.openbare_ruimte
? `${address.openbare_ruimte} ${address.huisnummer}${address.huisletter}${toevoeging}, ${address.postcode} ${address.woonplaats}`
: '_';
return display;
};

export default mapLocation;
119 changes: 81 additions & 38 deletions src/shared/services/map-location/index.test.js
@@ -1,52 +1,95 @@
import mapLocation from './index';
import mapLocation, { formatAddress, feature2location, address2pdok } from './index';

const testAddress = {
openbare_ruimte: 'Keizersgracht',
huisnummer: 666,
huisletter: 'D',
huisnummer_toevoeging: 3,
postcode: '1016EJ',
woonplaats: 'Amsterdam',
};

const testPdokAddress = {
straatnaam: 'Keizersgracht',
huisnummer: '666',
huisletter: 'D',
huisnummertoevoeging: '3',
postcode: '1016EJ',
woonplaatsnaam: 'Amsterdam',
};

const testLocation = { lng: 4, lat: 52 };

const testFeature = {
type: 'Point',
coordinates: [4, 52],
};

describe('feature2location', () => {
it('should convert', () => {
expect(feature2location(testFeature)).toEqual(testLocation);
});
});

describe('address2pdok', () => {
it('should convert', () => {
expect(address2pdok(testAddress)).toEqual(testPdokAddress);
});
});

describe('The map location service', () => {
it('should map geometry', () => {
expect(mapLocation({
query: {
longitude: 4,
latitude: 52,
},
})).toEqual({
geometrie: {
type: 'Point',
coordinates: [
4,
52,
],
expect(
mapLocation({
geometrie: {
type: 'Point',
coordinates: [4, 52],
},
})
).toEqual({
location: {
lat: 52,
lng: 4,
},
});
});

it('should map omgevingsinfo', () => {
expect(mapLocation({
omgevingsinfo: {
buurtcode: 'A02d',
stadsdeelcode: 'A',
},
})).toEqual({
buurt_code: 'A02d',
stadsdeel: 'A',
expect(
mapLocation({
buurt_code: 'A02d',
stadsdeel: 'A',
})
).toEqual({
buurtcode: 'A02d',
stadsdeelcode: 'A',
});
});

it('should map dichtstbijzijnd_adres', () => {
expect(mapLocation({
dichtstbijzijnd_adres: {
openbare_ruimte: 'Keizersgracht',
huisnummer: 666,
huisletter: 'D',
huisnummer_toevoeging: 3,
postcode: '1016EJ',
},
})).toEqual({
address: {
openbare_ruimte: 'Keizersgracht',
huisnummer: '666',
huisletter: 'D',
huisnummer_toevoeging: '3',
postcode: '1016EJ',
},
it('should convert map adress', () => {
expect(
mapLocation({
address: testAddress,
})
).toEqual({
address: testPdokAddress,
});
});
});

describe('The formatAddress', () => {
it('should render an _ when no data', () => {
expect(formatAddress({})).toEqual('_');
});

it('should render the address name', () => {
expect(formatAddress(testAddress)).toEqual('Keizersgracht 666D-3, 1016EJ Amsterdam');
});

it('should render the address without toevoeging', () => {
expect(formatAddress(testAddress)).toEqual('Keizersgracht 666D-3, 1016EJ Amsterdam');
expect(formatAddress({ ...testAddress, huisnummer_toevoeging: null })).toEqual(
'Keizersgracht 666D, 1016EJ Amsterdam'
);
});
});