Skip to content

Commit a890400

Browse files
committed
feature #2762 [Map] Add multipolygon support (Danny van Wijk)
This PR was merged into the 2.x branch. Discussion ---------- [Map] Add multipolygon support | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Docs? | yes/no <!-- required for new features --> | Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - For new features, provide some code snippets to help understand usage. - Features and deprecations must be submitted against branch main. - Update/add documentation as required (we can help!) - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> Add support for MultiPolygon https://leafletjs.com/reference.html#polygon & https://developers.google.com/maps/documentation/javascript/shapes?hl=fr#polygon_hole <img width="834" alt="image" src="https://github.com/user-attachments/assets/145ce748-2feb-449f-b39e-87c07cb25d1e" /> <img width="817" alt="image" src="https://github.com/user-attachments/assets/e1d83b77-26b1-46be-9093-108739dd5584" /> Commits ------- a9f1322 [Map] Add polygones-with-holes support
2 parents 5c1c35c + a9f1322 commit a890400

File tree

6 files changed

+219
-12
lines changed

6 files changed

+219
-12
lines changed

src/Map/CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# CHANGELOG
22

3+
## 2.26
4+
5+
- Add support for creating `Polygon` with holes, by passing an array of `array<Point>` as `points` parameter to the `Polygon` constructor, e.g.:
6+
```php
7+
// Draw a polygon with a hole in it, on the French map
8+
$map->addPolygon(new Polygon(points: [
9+
// First path, the outer boundary of the polygon
10+
[
11+
new Point(48.117266, -1.677792), // Rennes
12+
new Point(50.629250, 3.057256), // Lille
13+
new Point(48.573405, 7.752111), // Strasbourg
14+
new Point(43.296482, 5.369780), // Marseille
15+
new Point(44.837789, -0.579180), // Bordeaux
16+
],
17+
// Second path, it will make a hole in the previous one
18+
[
19+
new Point(45.833619, 1.261105), // Limoges
20+
new Point(45.764043, 4.835659), // Lyon
21+
new Point(49.258329, 4.031696), // Reims
22+
new Point(48.856613, 2.352222), // Paris
23+
],
24+
]));
25+
```
26+
327
## 2.25
428

529
- Downgrade PHP requirement from 8.3 to 8.1

src/Map/assets/dist/abstract_map_controller.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = WithIdentifier<
3636
}>;
3737
export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = WithIdentifier<{
3838
infoWindow?: InfoWindowWithoutPositionDefinition<InfoWindowOptions>;
39-
points: Array<Point>;
39+
points: Array<Point> | Array<Array<Point>>;
4040
title: string | null;
4141
rawOptions?: PolygonOptions;
4242
extra: Record<string, unknown>;

src/Map/assets/src/abstract_map_controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = WithIdentifier<
4848

4949
export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = WithIdentifier<{
5050
infoWindow?: InfoWindowWithoutPositionDefinition<InfoWindowOptions>;
51-
points: Array<Point>;
51+
points: Array<Point> | Array<Array<Point>>;
5252
title: string | null;
5353
/**
5454
* Raw options passed to the marker constructor, specific to the map provider (e.g.: `L.marker()` for Leaflet).

src/Map/doc/index.rst

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Start by creating a new map instance::
7171
use Symfony\UX\Map\Map;
7272

7373
// Create a new map instance
74-
$myMap = (new Map());
74+
$map = new Map();
7575

7676
Center and zoom
7777
~~~~~~~~~~~~~~~
@@ -81,7 +81,7 @@ You can set the center and zoom of the map using the ``center()`` and ``zoom()``
8181
use Symfony\UX\Map\Map;
8282
use Symfony\UX\Map\Point;
8383

84-
$myMap
84+
$map
8585
// Explicitly set the center and zoom
8686
->center(new Point(46.903354, 1.888334))
8787
->zoom(6)
@@ -95,7 +95,7 @@ Add markers
9595

9696
You can add markers to a map using the ``addMarker()`` method::
9797

98-
$myMap
98+
$map
9999
->addMarker(new Marker(
100100
position: new Point(48.8566, 2.3522),
101101
title: 'Paris'
@@ -154,7 +154,7 @@ Add Polygons
154154

155155
You can also add Polygons, which represents an area enclosed by a series of ``Point`` instances::
156156

157-
$myMap->addPolygon(new Polygon(
157+
$map->addPolygon(new Polygon(
158158
points: [
159159
new Point(48.8566, 2.3522),
160160
new Point(45.7640, 4.8357),
@@ -166,12 +166,37 @@ You can also add Polygons, which represents an area enclosed by a series of ``Po
166166
),
167167
));
168168

169+
.. versionadded:: 2.26
170+
171+
`Polygon` with holes is available since UX Map 2.26.
172+
173+
Since UX Map 2.26, you can also create polygons with holes in them, by passing an array of `array<Point>` to `points` parameter::
174+
175+
// Draw a polygon with a hole in it, on the French map
176+
$map->addPolygon(new Polygon(points: [
177+
// First path, the outer boundary of the polygon
178+
[
179+
new Point(48.117266, -1.677792), // Rennes
180+
new Point(50.629250, 3.057256), // Lille
181+
new Point(48.573405, 7.752111), // Strasbourg
182+
new Point(43.296482, 5.369780), // Marseille
183+
new Point(44.837789, -0.579180), // Bordeaux
184+
],
185+
// Second path, it will make a hole in the previous one
186+
[
187+
new Point(45.833619, 1.261105), // Limoges
188+
new Point(45.764043, 4.835659), // Lyon
189+
new Point(49.258329, 4.031696), // Reims
190+
new Point(48.856613, 2.352222), // Paris
191+
],
192+
]));
193+
169194
Add Polylines
170195
~~~~~~~~~~~~~
171196

172197
You can add Polylines, which represents a path made by a series of ``Point`` instances::
173198

174-
$myMap->addPolyline(new Polyline(
199+
$map->addPolyline(new Polyline(
175200
points: [
176201
new Point(48.8566, 2.3522),
177202
new Point(45.7640, 4.8357),

src/Map/src/Polygon.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
final class Polygon implements Element
2222
{
2323
/**
24-
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
24+
* @param array<Point>|array<array<Point>> $points A list of point representing the polygon, or a list of paths (each path is an array of points) representing a polygon with holes.
25+
* @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
2526
*/
2627
public function __construct(
2728
private readonly array $points,
@@ -36,7 +37,7 @@ public function __construct(
3637
* Convert the polygon to an array representation.
3738
*
3839
* @return array{
39-
* points: array<array{lat: float, lng: float}>,
40+
* points: array<array{lat: float, lng: float}>|array<array{lat: float, lng: float}>,
4041
* title: string|null,
4142
* infoWindow: array<string, mixed>|null,
4243
* extra: array,
@@ -46,7 +47,9 @@ public function __construct(
4647
public function toArray(): array
4748
{
4849
return [
49-
'points' => array_map(fn (Point $point) => $point->toArray(), $this->points),
50+
'points' => current($this->points) instanceof Point
51+
? array_map(fn (Point $point) => $point->toArray(), $this->points)
52+
: array_map(fn (array $path) => array_map(fn (Point $point) => $point->toArray(), $path), $this->points),
5053
'title' => $this->title,
5154
'infoWindow' => $this->infoWindow?->toArray(),
5255
'extra' => $this->extra,
@@ -56,7 +59,7 @@ public function toArray(): array
5659

5760
/**
5861
* @param array{
59-
* points: array<array{lat: float, lng: float}>,
62+
* points: array<array{lat: float, lng: float}|array<array{lat: float, lng: float}>>,
6063
* title: string|null,
6164
* infoWindow: array<string, mixed>|null,
6265
* extra: array,
@@ -70,7 +73,10 @@ public static function fromArray(array $polygon): self
7073
if (!isset($polygon['points'])) {
7174
throw new InvalidArgumentException('The "points" parameter is required.');
7275
}
73-
$polygon['points'] = array_map(Point::fromArray(...), $polygon['points']);
76+
77+
$polygon['points'] = isset($polygon['points'][0]['lat'], $polygon['points'][0]['lng'])
78+
? array_map(Point::fromArray(...), $polygon['points'])
79+
: array_map(fn(array $points) => array_map(Point::fromArray(...), $points), $polygon['points']);
7480

7581
if (isset($polygon['infoWindow'])) {
7682
$polygon['infoWindow'] = InfoWindow::fromArray($polygon['infoWindow']);

src/Map/tests/PolygonTest.php

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Map\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\UX\Map\Exception\InvalidArgumentException;
16+
use Symfony\UX\Map\InfoWindow;
17+
use Symfony\UX\Map\Point;
18+
use Symfony\UX\Map\Polygon;
19+
20+
class PolygonTest extends TestCase
21+
{
22+
public function testToArray()
23+
{
24+
$point1 = new Point(1.1, 2.2);
25+
$point2 = new Point(3.3, 4.4);
26+
27+
$infoWindow = new InfoWindow('info content');
28+
29+
$polygon = new Polygon(
30+
points: [$point1, $point2],
31+
title: 'Test Polygon',
32+
infoWindow: $infoWindow,
33+
extra: ['foo' => 'bar'],
34+
id: 'poly1'
35+
);
36+
37+
$array = $polygon->toArray();
38+
$this->assertSame([
39+
'points' => [['lat' => 1.1, 'lng' => 2.2], ['lat' => 3.3, 'lng' => 4.4]],
40+
'title' => 'Test Polygon',
41+
'infoWindow' => [
42+
'headerContent' => 'info content',
43+
'content' => null,
44+
'position' => null,
45+
'opened' => false,
46+
'autoClose' => true,
47+
'extra' => $array['infoWindow']['extra'],
48+
],
49+
'extra' => ['foo' => 'bar'],
50+
'id' => 'poly1',
51+
], $array);
52+
}
53+
54+
public function testToArrayMultidimensional()
55+
{
56+
$point1 = new Point(1.1, 2.2);
57+
$point2 = new Point(3.3, 4.4);
58+
$point3 = new Point(5.5, 6.6);
59+
60+
$polygon = new Polygon(
61+
points: [[$point1, $point2], [$point3]],
62+
);
63+
64+
$array = $polygon->toArray();
65+
$this->assertSame([
66+
'points' => [
67+
[['lat' => 1.1, 'lng' => 2.2], ['lat' => 3.3, 'lng' => 4.4]],
68+
[['lat' => 5.5, 'lng' => 6.6]],
69+
],
70+
'title' => null,
71+
'infoWindow' => null,
72+
'extra' => $array['extra'],
73+
'id' => null,
74+
], $array);
75+
}
76+
77+
public function testFromArray()
78+
{
79+
$data = [
80+
'points' => [
81+
['lat' => 1.1, 'lng' => 2.2], ['lat' => 3.3, 'lng' => 4.4],
82+
],
83+
'title' => 'Test Polygon',
84+
'infoWindow' => ['content' => 'info content'],
85+
'extra' => ['foo' => 'bar'],
86+
'id' => 'poly1',
87+
];
88+
89+
$polygon = Polygon::fromArray($data);
90+
91+
$this->assertInstanceOf(Polygon::class, $polygon);
92+
93+
$array = $polygon->toArray();
94+
$this->assertSame([
95+
'points' => [['lat' => 1.1, 'lng' => 2.2], ['lat' => 3.3, 'lng' => 4.4]],
96+
'title' => 'Test Polygon',
97+
'infoWindow' => [
98+
'headerContent' => null,
99+
'content' => 'info content',
100+
'position' => null,
101+
'opened' => false,
102+
'autoClose' => true,
103+
'extra' => $array['infoWindow']['extra'],
104+
],
105+
'extra' => ['foo' => 'bar'],
106+
'id' => 'poly1',
107+
], $array);
108+
}
109+
110+
public function testFromArrayMultidimensional()
111+
{
112+
$data = [
113+
'points' => [
114+
[['lat' => 1.1, 'lng' => 2.2], ['lat' => 3.3, 'lng' => 4.4]],
115+
[['lat' => 5.5, 'lng' => 6.6]],
116+
],
117+
'title' => 'Test Polygon',
118+
'infoWindow' => ['content' => 'info content'],
119+
'extra' => ['foo' => 'bar'],
120+
'id' => 'poly1',
121+
];
122+
123+
$polygon = Polygon::fromArray($data);
124+
125+
$this->assertInstanceOf(Polygon::class, $polygon);
126+
127+
$array = $polygon->toArray();
128+
$this->assertSame([
129+
'points' => [
130+
[['lat' => 1.1, 'lng' => 2.2], ['lat' => 3.3, 'lng' => 4.4]],
131+
[['lat' => 5.5, 'lng' => 6.6]],
132+
],
133+
'title' => 'Test Polygon',
134+
'infoWindow' => [
135+
'headerContent' => null,
136+
'content' => 'info content',
137+
'position' => null,
138+
'opened' => false,
139+
'autoClose' => true,
140+
'extra' => $array['infoWindow']['extra'],
141+
],
142+
'extra' => ['foo' => 'bar'],
143+
'id' => 'poly1',
144+
], $array);
145+
}
146+
147+
public function testFromArrayThrowsExceptionIfPointsMissing()
148+
{
149+
$this->expectException(InvalidArgumentException::class);
150+
Polygon::fromArray(['invalid' => 'No points']);
151+
}
152+
}

0 commit comments

Comments
 (0)