├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── bin └── build.js ├── composer.json ├── config └── filament-map-picker.php ├── package-lock.json ├── postcss.config.cjs ├── resources ├── css │ └── index.css ├── dist │ ├── filament-map-picker.css │ ├── filament-map-picker.css.map │ └── filament-map-picker.js ├── js │ └── index.js ├── lang │ └── en │ │ └── map-picker.php └── views │ ├── .gitkeep │ ├── fields │ └── osm-map-picker.blade.php │ └── infolists │ └── osm-map-entry.blade.php └── src ├── Contracts └── MapOptions.php ├── Facades └── MapPicker.php ├── Fields └── Map.php ├── Infolists └── MapEntry.php └── MapPickerServiceProvider.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `map-picker` will be documented in this file. 4 | 5 | ## 1.0.0 - 202X-XX-XX 6 | 7 | - initial release 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) dotswan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Filament V3 Map Picker 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Total Downloads][ico-downloads]][link-downloads] 5 | [![Software License][ico-license]][link-license] 6 | 7 | 8 | A custom field for Filament that allows you to effortlessly select a location on a map and retrieve geographical coordinates. 9 | 10 | 11 | ![270298161-46b97f72-518b-40c5-963b-8e9d39d77d67](https://github.com/dotswan/map-picker/assets/20874565/a5dbda7b-b5c1-4038-9bf9-7a0a4c8ff632) 12 | 13 | ![image](https://github.com/user-attachments/assets/53d9de27-7e1f-4638-8c71-3b2c6b5d68ef) 14 | 15 | ## Introduction 16 | 17 | Map Picker is a Filament custom field designed to simplify the process of choosing a location on a map and obtaining its geo-coordinates. 18 | 19 | * Features include: 20 | * A Field for Filament-v3 with OpenStreetMap Integration 21 | * Receive Real-time Coordinates Upon Marker Movement Completion 22 | * Tailor Controls and Marker Appearance to Your Preferences 23 | * GeoMan Integration for Advanced Map Editing Capabilities 24 | 25 | * Latest versions of PHP and Filament 26 | * Best practices applied: 27 | * [`README.md`][link-readme] (badges included) 28 | * [`LICENSE`][link-license] 29 | * [`composer.json`][link-composer-json] 30 | * [`.gitignore`][link-gitignore] 31 | * [`pint.json`][link-pint] 32 | 33 | 34 | ## GeoMan Integration 35 | 36 | This package now includes integration with GeoMan, a powerful tool for creating and editing geometries on maps. GeoMan allows users to draw various shapes, edit existing geometries, and perform advanced map editing tasks. 37 | 38 | ### GeoMan Features: 39 | 40 | - Draw markers, polygons, polylines, and circles 41 | - Edit existing geometries 42 | - Cut polygons 43 | - Rotate shapes 44 | - Drag mode for easy shape manipulation 45 | - Delete layers 46 | 47 | ## Supported Maps 48 | 49 | Map Picker currently supports the following map: 50 | 51 | 1. Open Street Map (OSM) 52 | 53 | Additional map options will be added to the package as needed and tested. 54 | 55 | ## Installation 56 | 57 | You can easily install the package via Composer: 58 | 59 | ```bash 60 | composer require dotswan/filament-map-picker 61 | ``` 62 | 63 | ## Basic Usage 64 | 65 | Resource file: 66 | 67 | ```php 68 | schema([ 81 | Map::make('location') 82 | ->label('Location') 83 | ->columnSpanFull() 84 | // Basic Configuration 85 | ->defaultLocation(latitude: 40.4168, longitude: -3.7038) 86 | ->draggable(true) 87 | ->clickable(true) // click to move marker 88 | ->zoom(15) 89 | ->minZoom(0) 90 | ->maxZoom(28) 91 | ->tilesUrl("https://tile.openstreetmap.de/{z}/{x}/{y}.png") 92 | ->detectRetina(true) 93 | 94 | // Marker Configuration 95 | ->showMarker(true) 96 | ->markerColor("#3b82f6") 97 | ->markerHtml('
...
') 98 | ->markerIconUrl('/path/to/marker.png') 99 | ->markerIconSize([36, 36]) 100 | ->markerIconClassName('my-marker-class') 101 | ->markerIconAnchor([18, 36]) 102 | 103 | // Controls 104 | ->showFullscreenControl(true) 105 | ->showZoomControl(true) 106 | 107 | // Location Features 108 | ->liveLocation(true, true, 5000) 109 | ->showMyLocationButton(true) 110 | ->boundaries(true, 49.5, -11, 61, 2) // Example for British Isles 111 | ->rangeSelectField('distance') 112 | 113 | // GeoMan Integration 114 | ->geoMan(true) 115 | ->geoManEditable(true) 116 | ->geoManPosition('topleft') 117 | ->drawCircleMarker(true) 118 | ->rotateMode(true) 119 | ->drawMarker(true) 120 | ->drawPolygon(true) 121 | ->drawPolyline(true) 122 | ->drawCircle(true) 123 | ->drawRectangle(true) 124 | ->drawText(true) 125 | ->dragMode(true) 126 | ->cutPolygon(true) 127 | ->editPolygon(true) 128 | ->deleteLayer(true) 129 | ->setColor('#3388ff') 130 | ->setFilledColor('#cad9ec') 131 | ->snappable(true, 20) 132 | 133 | // Extra Customization 134 | ->extraStyles([ 135 | 'min-height: 150vh', 136 | 'border-radius: 50px' 137 | ]) 138 | ->extraControl(['customControl' => true]) 139 | ->extraTileControl(['customTileOption' => 'value']) 140 | 141 | // State Management 142 | ->afterStateUpdated(function (Set $set, ?array $state): void { 143 | $set('latitude', $state['lat']); 144 | $set('longitude', $state['lng']); 145 | $set('geojson', json_encode($state['geojson'])); 146 | }) 147 | ->afterStateHydrated(function ($state, $record, Set $set): void { 148 | $set('location', [ 149 | 'lat' => $record->latitude, 150 | 'lng' => $record->longitude, 151 | 'geojson' => json_decode(strip_tags($record->description)) 152 | ]); 153 | }) 154 | ]); 155 | } 156 | ... 157 | } 158 | ``` 159 | 160 | If you wish to update the map location and marker either through an action or after altering other input values, you can trigger a refresh of the map using the following approach: 161 | 162 | ```php 163 | 164 | use Filament\Forms\Components\Actions\Action; 165 | use Filament\Forms\Components\Actions; 166 | use Filament\Support\Enums\VerticalAlignment; 167 | 168 | Actions::make([ 169 | Action::make('Set Default Location') 170 | ->icon('heroicon-m-map-pin') 171 | ->action(function (Set $set, $state, $livewire): void { 172 | $set('location', ['lat' => '52.35510989541003', 'lng' => '4.883422851562501']); 173 | $set('latitude', '52.35510989541003'); 174 | $set('longitude', '4.883422851562501'); 175 | $livewire->dispatch('refreshMap'); 176 | }) 177 | ])->verticalAlignment(VerticalAlignment::Start); 178 | 179 | ``` 180 | 181 | ### clickable Option 182 | 183 | This will allow you to set the point on the map with a click. Default behaviour has the marker centered as the map is 184 | dragged underneath. You could, with this, keep the map still and lock the zoom and choose to click to place the marker. 185 | 186 | ```php 187 | Map::make('location') 188 | ->defaultLocation(latitude: 40.4168, longitude: -3.7038) 189 | ->showMarker(true) 190 | ->clickable(true) 191 | ->tilesUrl("https://tile.openstreetmap.de/{z}/{x}/{y}.png") 192 | ->zoom(12) 193 | ``` 194 | 195 | 196 | ### rangeSelectField Option 197 | 198 | The rangeSelectField Option allows you to specify another field on your form which specifies a range from the point 199 | identified by the marker. That field must be in meters. So for example you could do this: 200 | 201 | ```php 202 | Fieldset::make('Location') 203 | ->schema([ 204 | Select::make('membership_distance') 205 | ->enum(MembershipDistance::class) 206 | ->options(MembershipDistance::class) 207 | ->required(), 208 | 209 | Map::make('location') 210 | ->defaultLocation(latitude: 40.4168, longitude: -3.7038) 211 | ->showMarker(true) 212 | ->showFullscreenControl(false) 213 | ->showZoomControl() 214 | ->tilesUrl("https://tile.openstreetmap.de/{z}/{x}/{y}.png") 215 | ->zoom(12) 216 | ->detectRetina() 217 | ->rangeSelectField('membership_distance') 218 | ->setFilledColor('#cad9ec'), 219 | ]) 220 | ->columns(1), 221 | ``` 222 | 223 | In this case, as you change the value on the Select a circle of that radius centered on the marker will 224 | change to match your drop down. 225 | 226 | 227 | #### `liveLocation` Option 228 | 229 | The `liveLocation` method accepts three parameters: 230 | 231 | 1. **`bool $send`:** Determines if the user's live location should be sent. 232 | 2. **`bool $realtime`:** Controls whether the live location should be sent to the server periodically. 233 | 3. **`int $milliseconds`:** Sets the interval (in milliseconds) at which the user's location is updated and sent to the server. 234 | 235 | Example: 236 | 237 | ```php 238 | Map::make('location') 239 | ->liveLocation(true, true, 10000) // Updates live location every 10 seconds 240 | ->showMarker() 241 | ->draggable() 242 | ``` 243 | 244 | ### boundaries Option 245 | 246 | The idea here is that you can set a boundary box by defining two points, the southwest most point and the north east 247 | most point, and your map will pan back into the panned area if you drag away, such that the points can only be selected 248 | if you stay in the map. 249 | 250 | You will want to set the minZoom() along with this if you set showZoomControl(true). To choose a good value for minZoom() 251 | you will need to consider both the size of the map on the screen and the size of the bounding boxm, and you may find trial and 252 | error is the best method. 253 | 254 | ```php 255 | Map::make('location') 256 | ->showMarker() 257 | ->boundaries(true,49,11.1,61.0,2.1) 258 | ->draggable() 259 | ``` 260 | 261 | To turn it off again - possibly a strange use case - `boundaries(false)` is what you want. 262 | 263 | 264 | ### setBoundsToBritishIsles Option 265 | 266 | This is a convenience function that uses the boundaries option above, setting the boundary box to 267 | (49.5,-11) and (61,2) 268 | 269 | 270 | ## Options Table 271 | 272 | Here's a table describing all available options and their default values: 273 | 274 | | Option | Description | Default Value | 275 | |--------|-------------|---------------| 276 | | statePath | Path to the state | '' | 277 | | draggable | Allow map dragging | true | 278 | | showMarker | Display marker on the map | true | 279 | | tilesUrl | URL for map tiles | 'http://tile.openstreetmap.org/{z}/{x}/{y}.png' | 280 | | attribution | Map attribution text | null | 281 | | zoomOffset | Zoom offset | -1 | 282 | | tileSize | Tile size | 512 | 283 | | detectRetina | Detect and use retina tiles | true | 284 | | rangeSelectField | Field name for range selection | 'distance' | 285 | | minZoom | Minimum zoom level | 0 | 286 | | maxZoom | Maximum zoom level | 28 | 287 | | zoom | Default zoom level | 15 | 288 | | clickable | Allow clicking to place marker | false | 289 | | markerColor | Color of the marker | '#3b82f6' | 290 | | liveLocation | Enable live location updates | false | 291 | | bounds | Enable map boundaries | false | 292 | | showMyLocationButton | Show "My Location" button settings | [false, false, 5000] | 293 | | default | Default location coordinates | ['lat' => 0, 'lng' => 0] | 294 | | markerHtml | Custom HTML for marker | '' | 295 | | markerIconUrl | URL for custom marker icon | null | 296 | | markerIconSize | Size of marker icon | [36, 36] | 297 | | markerIconClassName | CSS class for marker icon | '' | 298 | | markerIconAnchor | Anchor point for marker icon | [18, 36] | 299 | | geoMan.show | Enable GeoMan | false | 300 | | geoMan.editable | Allow editing with GeoMan | true | 301 | | geoMan.position | Position of GeoMan controls | 'topleft' | 302 | | geoMan.drawCircleMarker | Allow drawing circle markers | true | 303 | | geoMan.rotateMode | Enable rotate mode | true | 304 | | geoMan.drawMarker | Allow drawing markers | true | 305 | | geoMan.drawPolygon | Allow drawing polygons | true | 306 | | geoMan.drawPolyline | Allow drawing polylines | true | 307 | | geoMan.drawCircle | Allow drawing circles | true | 308 | | geoMan.dragMode | Enable drag mode | true | 309 | | geoMan.cutPolygon | Allow cutting polygons | true | 310 | | geoMan.editPolygon | Allow editing polygons | true | 311 | | geoMan.deleteLayer | Allow deleting layers | true | 312 | | geoMan.color | Stroke color for drawings | '#3388ff' | 313 | | geoMan.filledColor | Fill color for drawings | '#cad9ec' | 314 | | geoMan.snappable | Enable snapping to objects | false | 315 | | geoMan.snapDistance | Distance for snapping | 20 | 316 | | geoMan.drawText | Allow drawing text | true | 317 | | geoMan.drawRectangle | Allow drawing rectangles | true | 318 | 319 | ### Usage As Infolist Field 320 | 321 | The MapEntry Infolist field displays a map with all the same configuration options available in the form field. Here's an example: 322 | 323 | ```php 324 | use Dotswan\MapPicker\Infolists\MapEntry; 325 | 326 | public static function infolist(Infolist $infolist): Infolist 327 | { 328 | return $infolist 329 | ->schema([ 330 | MapEntry::make('location') 331 | // Basic Configuration 332 | ->defaultLocation(latitude: 40.4168, longitude: -3.7038) 333 | ->draggable(false) // Usually false for infolist view 334 | ->zoom(15) 335 | ->minZoom(0) 336 | ->maxZoom(28) 337 | ->tilesUrl("https://tile.openstreetmap.de/{z}/{x}/{y}.png") 338 | ->detectRetina(true) 339 | 340 | // Marker Configuration 341 | ->showMarker(true) 342 | ->markerColor("#22c55eff") 343 | ->markerHtml('
...
') 344 | ->markerIconUrl('/path/to/marker.png') 345 | ->markerIconSize([36, 36]) 346 | ->markerIconClassName('my-marker-class') 347 | ->markerIconAnchor([18, 36]) 348 | 349 | // Controls 350 | ->showFullscreenControl(true) 351 | ->showZoomControl(true) 352 | 353 | // GeoMan Integration (if needed for viewing) 354 | ->geoMan(true) 355 | ->geoManEditable(false) // Usually false for infolist view 356 | ->geoManPosition('topleft') 357 | ->drawCircleMarker(true) 358 | ->drawMarker(true) 359 | ->drawPolygon(true) 360 | ->drawPolyline(true) 361 | ->drawCircle(true) 362 | ->drawRectangle(true) 363 | ->drawText(true) 364 | 365 | // Styling 366 | ->extraStyles([ 367 | 'min-height: 50vh', 368 | 'border-radius: 50px' 369 | ]) 370 | 371 | // State Management 372 | ->state(fn ($record) => [ 373 | 'lat' => $record?->latitude, 374 | 'lng' => $record?->longitude, 375 | 'geojson' => $record?->geojson ? json_decode($record->geojson) : null 376 | ]) 377 | ]); 378 | } 379 | ``` 380 | 381 | Note: In infolist context, it's common to: 382 | - Set `draggable(false)` since it's typically used for viewing only 383 | - Set `geoManEditable(false)` if GeoMan is enabled 384 | - Use `state()` instead of `afterStateHydrated()`/`afterStateUpdated()` 385 | - Adjust the height to be smaller than in forms (e.g., 50vh vs 150vh) 386 | 387 | ## Usage Guide for Handling Map Locations 388 | 389 | This section explains how to handle and display map locations within your application using this package. 390 | 391 | **Step 1: Define Your Database Schema** 392 | 393 | Ensure your database table includes latitude and longitude columns. 394 | This is essential for storing the coordinates of your locations. You can define your table schema as follows: 395 | 396 | ```php 397 | $table->double('latitude')->nullable(); 398 | $table->double('longitude')->nullable(); 399 | ``` 400 | 401 | **Step 2: Retrieve and Set Coordinates** 402 | 403 | When loading a record, ensure you correctly retrieve and set the latitude and longitude values. 404 | Use the following method within your form component: 405 | 406 | ```php 407 | ->afterStateHydrated(function ($state, $record, Set $set): void { 408 | $set('location', ['lat' => $record?->latitude, 'lng' => $record?->longitude]); 409 | }) 410 | ``` 411 | 412 | **Step 3: Add Form Fields for Latitude and Longitude** 413 | 414 | Add hidden form fields for latitude and longitude to your form. This ensures the values are present but not visible to the user: 415 | 416 | ```php 417 | TextInput::make('latitude') 418 | ->hiddenLabel() 419 | ->hidden(), 420 | 421 | TextInput::make('longitude') 422 | ->hiddenLabel() 423 | ->hidden() 424 | ``` 425 | 426 | If you prefer to display these values in a read-only format, replace `hidden()` with `readOnly()`. 427 | 428 | ### Alternative Approach: Using a Single Location Attribute 429 | 430 | If you prefer to handle the location as a single field, you can define a custom attribute in your model. This method avoids the need for separate latitude and longitude columns: 431 | 432 | ```php 433 | class YourModel extends Model 434 | { 435 | protected function location(): Attribute 436 | { 437 | return Attribute::make( 438 | get: fn (mixed $value, array $attributes) => [ 439 | 'latitude' => $attributes['latitude'], 440 | 'longitude' => $attributes['longitude'] 441 | ], 442 | set: fn (array $value) => [ 443 | 'latitude' => $value['latitude'], 444 | 'longitude' => $value['longitude'] 445 | ], 446 | ); 447 | } 448 | } 449 | ``` 450 | 451 | This approach encapsulates both latitude and longitude within a single location attribute, streamlining your code. 452 | 453 | 454 | 455 | ## License 456 | 457 | [MIT License](LICENSE.md) © Dotswan 458 | 459 | ## Security 460 | 461 | We take security seriously. If you discover any bugs or security issues, please help us maintain a secure project by reporting them through our [`GitHub issue tracker`][link-github-issue]. You can also contact us directly at [tech@dotswan.com](mailto:tech@dotswan.com). 462 | 463 | ## Contribution 464 | 465 | We welcome contributions! contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. 466 | 467 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again! 468 | 469 | 1. Fork the Project 470 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 471 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 472 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 473 | 5. Open a Pull Request 474 | 475 | 476 | [ico-version]: https://img.shields.io/packagist/v/dotswan/filament-map-picker.svg?style=flat-square 477 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 478 | [ico-downloads]: https://img.shields.io/packagist/dt/dotswan/filament-map-picker.svg?style=flat-square 479 | 480 | [link-workflow-test]: https://github.com/dotswan/filament-map-picker/actions/workflows/ci.yml 481 | [link-packagist]: https://packagist.org/packages/dotswan/filament-map-picker 482 | [link-license]: https://github.com/dotswan/filament-map-picker/blob/master/LICENSE.md 483 | [link-downloads]: https://packagist.org/packages/dotswan/filament-map-picker 484 | [link-readme]: https://github.com/dotswan/filament-map-picker/blob/master/README.md 485 | [link-github-issue]: https://github.com/dotswan/filament-map-picker/issues 486 | [link-docs]: https://github.com/dotswan/filament-map-picker/blob/master/docs/openapi.yaml 487 | [link-composer-json]: https://github.com/dotswan/filament-map-picker/blob/master/composer.json 488 | [link-gitignore]: https://github.com/dotswan/filament-map-picker/blob/master/.gitignore 489 | [link-pint]: https://github.com/dotswan/filament-map-picker/blob/master/pint.json 490 | [link-author]: https://github.com/dotswan 491 | -------------------------------------------------------------------------------- /bin/build.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild' 2 | 3 | const isDev = process.argv.includes('--dev') 4 | 5 | async function compile(options) { 6 | const context = await esbuild.context(options) 7 | 8 | if (isDev) { 9 | await context.watch() 10 | } else { 11 | await context.rebuild() 12 | await context.dispose() 13 | } 14 | } 15 | 16 | const defaultOptions = { 17 | define: { 18 | 'process.env.NODE_ENV': isDev ? `'development'` : `'production'`, 19 | }, 20 | bundle: true, 21 | mainFields: ['module', 'main'], 22 | platform: 'neutral', 23 | sourcemap: isDev ? 'inline' : false, 24 | sourcesContent: isDev, 25 | treeShaking: true, 26 | target: ['es2020'], 27 | minify: !isDev, 28 | plugins: [{ 29 | name: 'watchPlugin', 30 | setup: function (build) { 31 | build.onStart(() => { 32 | console.log(`Build started at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`) 33 | }) 34 | 35 | build.onEnd((result) => { 36 | if (result.errors.length > 0) { 37 | console.log(`Build failed at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`, result.errors) 38 | } else { 39 | console.log(`Build finished at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`) 40 | } 41 | }) 42 | } 43 | }], 44 | } 45 | 46 | compile({ 47 | ...defaultOptions, 48 | entryPoints: ['./resources/js/index.js'], 49 | outfile: './resources/dist/filament-map-picker.js', 50 | }) 51 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dotswan/filament-map-picker", 3 | "description": "Easily pick and retrieve geo-coordinates using a map-based interface in your Filament applications.", 4 | "keywords": [ 5 | "dotswan", 6 | "laravel", 7 | "map-picker", 8 | "filament", 9 | "filamentphp", 10 | "filament-map-picker", 11 | "filament-v3" 12 | ], 13 | "homepage": "https://github.com/dotswan/filament-map-picker", 14 | "support": { 15 | "issues": "https://github.com/dotswan/filament-map-picker/issues", 16 | "source": "https://github.com/dotswan/filament-map-picker" 17 | }, 18 | "license": "MIT", 19 | "authors": [ 20 | { 21 | "name": "Dotswan", 22 | "email": "tech@dotswan.com", 23 | "role": "Developer" 24 | } 25 | ], 26 | "require": { 27 | "php": "^8.1", 28 | "filament/filament": "^3.2.130", 29 | "spatie/laravel-package-tools": "^1.19.0", 30 | "illuminate/contracts": "^10.0 || ^11.0 || ^12.0" 31 | }, 32 | "require-dev": { 33 | "laravel/pint": "^1.0", 34 | "nunomaduro/collision": "^7.9", 35 | "orchestra/testbench": "^8.0|^9.0|^10.0", 36 | "pestphp/pest": "^2.1|^3.1", 37 | "pestphp/pest-plugin-arch": "^2.0", 38 | "pestphp/pest-plugin-laravel": "^2.0", 39 | "phpstan/extension-installer": "^1.1", 40 | "phpstan/phpstan-deprecation-rules": "^1.0", 41 | "phpstan/phpstan-phpunit": "^1.0" 42 | }, 43 | "autoload": { 44 | "psr-4": { 45 | "Dotswan\\MapPicker\\": "src/", 46 | "Dotswan\\MapPicker\\Database\\Factories\\": "database/factories/" 47 | } 48 | }, 49 | "autoload-dev": { 50 | "psr-4": { 51 | "Dotswan\\MapPicker\\Tests\\": "tests/" 52 | } 53 | }, 54 | "scripts": { 55 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 56 | "analyse": "vendor/bin/phpstan analyse", 57 | "test": "vendor/bin/pest", 58 | "test-coverage": "vendor/bin/pest --coverage", 59 | "format": "vendor/bin/pint" 60 | }, 61 | "config": { 62 | "sort-packages": true, 63 | "allow-plugins": { 64 | "pestphp/pest-plugin": true, 65 | "phpstan/extension-installer": true 66 | } 67 | }, 68 | "extra": { 69 | "laravel": { 70 | "providers": [ 71 | "Dotswan\\MapPicker\\MapPickerServiceProvider" 72 | ], 73 | "aliases": { 74 | "MapPicker": "Dotswan\\MapPicker\\Facades\\MapPicker" 75 | } 76 | } 77 | }, 78 | "minimum-stability": "stable", 79 | "prefer-stable": true 80 | } 81 | -------------------------------------------------------------------------------- /config/filament-map-picker.php: -------------------------------------------------------------------------------- 1 | canvas,.leaflet-pane>svg,.leaflet-tile,.leaflet-tile-container,.leaflet-zoom-box{position:absolute;left:0;top:0}.leaflet-container{overflow:hidden}.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-tile{-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none}.leaflet-tile::-moz-selection{background:#0000}.leaflet-tile::selection{background:#0000}.leaflet-safari .leaflet-tile{image-rendering:-webkit-optimize-contrast}.leaflet-safari .leaflet-tile-container{width:1600px;height:1600px;-webkit-transform-origin:0 0}.leaflet-marker-icon,.leaflet-marker-shadow{display:block}.leaflet-container .leaflet-overlay-pane svg{max-width:none!important;max-height:none!important}.leaflet-container .leaflet-marker-pane img,.leaflet-container .leaflet-shadow-pane img,.leaflet-container .leaflet-tile,.leaflet-container .leaflet-tile-pane img,.leaflet-container img.leaflet-image-layer{max-width:none!important;max-height:none!important;width:auto;padding:0}.leaflet-container img.leaflet-tile{mix-blend-mode:plus-lighter}.leaflet-container.leaflet-touch-zoom{touch-action:pan-x pan-y}.leaflet-container.leaflet-touch-drag{touch-action:none;touch-action:pinch-zoom}.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom{touch-action:none}.leaflet-container{-webkit-tap-highlight-color:transparent}.leaflet-container a{-webkit-tap-highlight-color:rgba(51,181,229,.4)}.leaflet-tile{filter:inherit;visibility:hidden}.leaflet-tile-loaded{visibility:inherit}.leaflet-zoom-box{width:0;height:0;box-sizing:border-box;z-index:800}.leaflet-overlay-pane svg{-moz-user-select:none}.leaflet-pane{z-index:400}.leaflet-tile-pane{z-index:200}.leaflet-overlay-pane{z-index:400}.leaflet-shadow-pane{z-index:500}.leaflet-marker-pane{z-index:600}.leaflet-tooltip-pane{z-index:650}.leaflet-popup-pane{z-index:700}.leaflet-map-pane canvas{z-index:100}.leaflet-map-pane svg{z-index:200}.leaflet-vml-shape{width:1px;height:1px}.lvml{behavior:url(#default#VML);display:inline-block;position:absolute}.leaflet-control{position:relative;z-index:800;pointer-events:visiblePainted;pointer-events:auto}.leaflet-bottom,.leaflet-top{position:absolute;z-index:1000;pointer-events:none}.leaflet-top{top:0}.leaflet-right{right:0}.leaflet-bottom{bottom:0}.leaflet-left{left:0}.leaflet-control{float:left;clear:both}.leaflet-right .leaflet-control{float:right}.leaflet-top .leaflet-control{margin-top:10px}.leaflet-bottom .leaflet-control{margin-bottom:10px}.leaflet-left .leaflet-control{margin-left:10px}.leaflet-right .leaflet-control{margin-right:10px}.leaflet-fade-anim .leaflet-popup{opacity:0;transition:opacity .2s linear}.leaflet-fade-anim .leaflet-map-pane .leaflet-popup{opacity:1}.leaflet-zoom-animated{transform-origin:0 0}svg.leaflet-zoom-animated{will-change:transform}.leaflet-zoom-anim .leaflet-zoom-animated{transition:transform .25s cubic-bezier(0,0,.25,1)}.leaflet-pan-anim .leaflet-tile,.leaflet-zoom-anim .leaflet-tile{transition:none}.leaflet-zoom-anim .leaflet-zoom-hide{visibility:hidden}.leaflet-interactive{cursor:pointer}.leaflet-grab{cursor:grab}.leaflet-crosshair,.leaflet-crosshair .leaflet-interactive{cursor:crosshair}.leaflet-control,.leaflet-popup-pane{cursor:auto}.leaflet-dragging .leaflet-grab,.leaflet-dragging .leaflet-grab .leaflet-interactive,.leaflet-dragging .leaflet-marker-draggable{cursor:move;cursor:grabbing}.leaflet-image-layer,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-pane>svg path,.leaflet-tile-container{pointer-events:none}.leaflet-image-layer.leaflet-interactive,.leaflet-marker-icon.leaflet-interactive,.leaflet-pane>svg path.leaflet-interactive,svg.leaflet-image-layer.leaflet-interactive path{pointer-events:visiblePainted;pointer-events:auto}.leaflet-container{background:#ddd;outline-offset:1px}.leaflet-container a{color:#0078a8}.leaflet-zoom-box{border:2px dotted #38f;background:#ffffff80}.leaflet-container{font-family:Helvetica Neue,Arial,Helvetica,sans-serif;font-size:12px;font-size:.75rem;line-height:1.5}.leaflet-bar{box-shadow:0 1px 5px #000000a6;border-radius:4px}.leaflet-bar a{background-color:#fff;border-bottom:1px solid #ccc;width:26px;height:26px;line-height:26px;display:block;text-align:center;text-decoration:none;color:#000}.leaflet-bar a,.leaflet-control-layers-toggle{background-position:50% 50%;background-repeat:no-repeat;display:block}.leaflet-bar a:focus,.leaflet-bar a:hover{background-color:#f4f4f4}.leaflet-bar a:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.leaflet-bar a:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-bottom:none}.leaflet-bar a.leaflet-disabled{cursor:default;background-color:#f4f4f4;color:#bbb}.leaflet-touch .leaflet-bar a{width:30px;height:30px;line-height:30px}.leaflet-touch .leaflet-bar a:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.leaflet-touch .leaflet-bar a:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.leaflet-control-zoom-in,.leaflet-control-zoom-out{font:700 18px Lucida Console,Monaco,monospace;text-indent:1px}.leaflet-touch .leaflet-control-zoom-in,.leaflet-touch .leaflet-control-zoom-out{font-size:22px}.leaflet-control-layers{box-shadow:0 1px 5px #0006;background:#fff;border-radius:5px}.leaflet-control-layers-toggle{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAQAAAADQ4RFAAACf0lEQVR4AY1UM3gkARTePdvdoTxXKc+qTl3aU5U6b2Kbkz3Gtq3Zw6ziLGNPzrYx7946Tr6/ee/XeCQ4D3ykPtL5tHno4n0d/h3+xfuWHGLX81cn7r0iTNzjr7LrlxCqPtkbTQEHeqOrTy4Yyt3VCi/IOB0v7rVC7q45Q3Gr5K6jt+3Gl5nCoDD4MtO+j96Wu8atmhGqcNGHObuf8OM/x3AMx38+4Z2sPqzCxRFK2aF2e5Jol56XTLyggAMTL56XOMoS1W4pOyjUcGGQdZxU6qRh7B9Zp+PfpOFlqt0zyDZckPi1ttmIp03jX8gyJ8a/PG2yutpS/Vol7peZIbZcKBAEEheEIAgFbDkz5H6Zrkm2hVWGiXKiF4Ycw0RWKdtC16Q7qe3X4iOMxruonzegJzWaXFrU9utOSsLUmrc0YjeWYjCW4PDMADElpJSSQ0vQvA1Tm6/JlKnqFs1EGyZiFCqnRZTEJJJiKRYzVYzJck2Rm6P4iH+cmSY0YzimYa8l0EtTODFWhcMIMVqdsI2uiTvKmTisIDHJ3od5GILVhBCarCfVRmo4uTjkhrhzkiBV7SsaqS+TzrzM1qpGGUFt28pIySQHR6h7F6KSwGWm97ay+Z+ZqMcEjEWebE7wxCSQwpkhJqoZA5ivCdZDjJepuJ9IQjGGUmuXJdBFUygxVqVsxFsLMbDe8ZbDYVCGKxs+W080max1hFCarCfV+C1KATwcnvE9gRRuMP2prdbWGowm1KB1y+zwMMENkM755cJ2yPDtqhTI6ED1M/82yIDtC/4j4BijjeObflpO9I9MwXTCsSX8jWAFeHr05WoLTJ5G8IQVS/7vwR6ohirYM7f6HzYpogfS3R2OAAAAAElFTkSuQmCC);width:36px;height:36px}.leaflet-retina .leaflet-control-layers-toggle{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAQAAABvcdNgAAAEsklEQVR4AWL4TydIhpZK1kpWOlg0w3ZXP6D2soBtG42jeI6ZmQTHzAxiTbSJsYLjO9HhP+WOmcuhciVnmHVQcJnp7DFvScowZorad/+V/fVzMdMT2g9Cv9guXGv/7pYOrXh2U+RRR3dSd9JRx6bIFc/ekqHI29JC6pJ5ZEh1yWkhkbcFeSjxgx3L2m1cb1C7bceyxA+CNjT/Ifff+/kDk2u/w/33/IeCMOSaWZ4glosqT3DNnNZQ7Cs58/3Ce5HL78iZH/vKVIaYlqzfdLu8Vi7dnvUbEza5Idt36tquZFldl6N5Z/POLof0XLK61mZCmJSWjVF9tEjUluu74IUXvgttuVIHE7YxSkaYhJZam7yiM9Pv82JYfl9nptxZaxMJE4YSPty+vF0+Y2up9d3wwijfjZbabqm/3bZ9ecKHsiGmRflnn1MW4pjHf9oLufyn2z3y1D6n8g8TZhxyzipLNPnAUpsOiuWimg52psrTZYnOWYNDTMuWBWa0tJb4rgq1UvmutpaYEbZlwU3CLJm/ayYjHW5/h7xWLn9Hh1vepDkyf7dE7MtT5LR4e7yYpHrkhOUpEfssBLq2pPhAqoSWKUkk7EDqkmK6RrCEzqDjhNDWNE+XSMvkJRDWlZTmCW0l0PHQGRZY5t1L83kT0Y3l2SItk5JAWHl2dCOBm+fPu3fo5/3v61RMCO9Jx2EEYYhb0rmNQMX/vm7gqOEJLcXTGw3CAuRNeyaPWwjR8PRqKQ1PDA/dpv+on9Shox52WFnx0KY8onHayrJzm87i5h9xGw/tfkev0jGsQizqezUKjk12hBMKJ4kbCqGPVNXudyyrShovGw5CgxsRICxF6aRmSjlBnHRzg7Gx8fKqEubI2rahQYdR1YgDIRQO7JvQyD52hoIQx0mxa0ODtW2Iozn1le2iIRdzwWewedyZzewidueOGqlsn1MvcnQpuVwLGG3/IR1hIKxCjelIDZ8ldqWz25jWAsnldEnK0Zxro19TGVb2ffIZEsIO89EIEDvKMPrzmBOQcKQ+rroye6NgRRxqR4U8EAkz0CL6uSGOm6KQCdWjvjRiSP1BPalCRS5iQYiEIvxuBMJEWgzSoHADcVMuN7IuqqTeyUPq22qFimFtxDyBBJEwNyt6TM88blFHao/6tWWhuuOM4SAK4EI4QmFHA+SEyWlp4EQoJ13cYGzMu7yszEIBOm2rVmHUNqwAIQabISNMRstmdhNWcFLsSm+0tjJH1MdRxO5Nx0WDMhCtgD6OKgZeljJqJKc9po8juskR9XN0Y1lZ3mWjLR9JCO1jRDMd0fpYC2VnvjBSEFg7wBENc0R9HFlb0xvF1+TBEpF68d+DHR6IOWVv2BECtxo46hOFUBd/APU57WIoEwJhIi2CdpyZX0m93BZicktMj1AS9dClteUFAUNUIEygRZCtik5zSxI9MubTBH1GOiHsiLJ3OCoSZkILa9PxiN0EbvhsAo8tdAf9Seepd36lGWHmtNANTv5Jd0z4QYyeo/UEJqxKRpg5LZx6btLPsOaEmdMyxYdlc8LMaJnikDlhclqmPiQnTEpLUIZEwkRagjYkEibQErwhkTAKCLQEbUgkzJQWc/0PstHHcfEdQ+UAAAAASUVORK5CYII=);background-size:26px 26px}.leaflet-touch .leaflet-control-layers-toggle{width:44px;height:44px}.leaflet-control-layers .leaflet-control-layers-list,.leaflet-control-layers-expanded .leaflet-control-layers-toggle{display:none}.leaflet-control-layers-expanded .leaflet-control-layers-list{display:block;position:relative}.leaflet-control-layers-expanded{padding:6px 10px 6px 6px;color:#333;background:#fff}.leaflet-control-layers-scrollbar{overflow-y:scroll;overflow-x:hidden;padding-right:5px}.leaflet-control-layers-selector{margin-top:2px;position:relative;top:1px}.leaflet-control-layers label{display:block;font-size:13px;font-size:1.08333em}.leaflet-control-layers-separator{height:0;border-top:1px solid #ddd;margin:5px -10px 5px -6px}.leaflet-default-icon-path{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=)}.leaflet-container .leaflet-control-attribution{background:#fff;background:#fffc;margin:0}.leaflet-control-attribution,.leaflet-control-scale-line{padding:0 5px;color:#333;line-height:1.4}.leaflet-control-attribution a{text-decoration:none}.leaflet-control-attribution a:focus,.leaflet-control-attribution a:hover{text-decoration:underline}.leaflet-attribution-flag{display:inline!important;vertical-align:initial!important;width:1em;height:.6669em}.leaflet-left .leaflet-control-scale{margin-left:5px}.leaflet-bottom .leaflet-control-scale{margin-bottom:5px}.leaflet-control-scale-line{border:2px solid #777;border-top:none;line-height:1.1;padding:2px 5px 1px;white-space:nowrap;box-sizing:border-box;background:#fffc;text-shadow:1px 1px #fff}.leaflet-control-scale-line:not(:first-child){border-top:2px solid #777;border-bottom:none;margin-top:-2px}.leaflet-control-scale-line:not(:first-child):not(:last-child){border-bottom:2px solid #777}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-attribution,.leaflet-touch .leaflet-control-layers{box-shadow:none}.leaflet-touch .leaflet-bar,.leaflet-touch .leaflet-control-layers{border:2px solid #0003;background-clip:padding-box}.leaflet-popup{position:absolute;text-align:center;margin-bottom:20px}.leaflet-popup-content-wrapper{padding:1px;text-align:left;border-radius:12px}.leaflet-popup-content{margin:13px 24px 13px 20px;line-height:1.3;font-size:13px;font-size:1.08333em;min-height:1px}.leaflet-popup-content p{margin:1.3em 0}.leaflet-popup-tip-container{width:40px;height:20px;position:absolute;left:50%;margin-top:-1px;margin-left:-20px;overflow:hidden;pointer-events:none}.leaflet-popup-tip{width:17px;height:17px;padding:1px;margin:-10px auto 0;pointer-events:auto;transform:rotate(45deg)}.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:#fff;color:#333;box-shadow:0 3px 14px #0006}.leaflet-container a.leaflet-popup-close-button{position:absolute;top:0;right:0;border:none;text-align:center;width:24px;height:24px;font:16px/24px Tahoma,Verdana,sans-serif;color:#757575;text-decoration:none;background:#0000}.leaflet-container a.leaflet-popup-close-button:focus,.leaflet-container a.leaflet-popup-close-button:hover{color:#585858}.leaflet-popup-scrolled{overflow:auto}.leaflet-oldie .leaflet-popup-content-wrapper{-ms-zoom:1}.leaflet-oldie .leaflet-popup-tip{width:24px;margin:0 auto;-ms-filter:"progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678,M12=0.70710678,M21=-0.70710678,M22=0.70710678)}.leaflet-oldie .leaflet-control-layers,.leaflet-oldie .leaflet-control-zoom,.leaflet-oldie .leaflet-popup-content-wrapper,.leaflet-oldie .leaflet-popup-tip{border:1px solid #999}.leaflet-div-icon{background:#fff;border:1px solid #666}.leaflet-tooltip{position:absolute;padding:6px;background-color:#fff;border:1px solid #fff;border-radius:3px;color:#222;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:none;box-shadow:0 1px 3px #0006}.leaflet-tooltip.leaflet-interactive{cursor:pointer;pointer-events:auto}.leaflet-tooltip-bottom:before,.leaflet-tooltip-left:before,.leaflet-tooltip-right:before,.leaflet-tooltip-top:before{position:absolute;pointer-events:none;border:6px solid #0000;background:#0000;content:""}.leaflet-tooltip-bottom{margin-top:6px}.leaflet-tooltip-top{margin-top:-6px}.leaflet-tooltip-bottom:before,.leaflet-tooltip-top:before{left:50%;margin-left:-6px}.leaflet-tooltip-top:before{bottom:0;margin-bottom:-12px;border-top-color:#fff}.leaflet-tooltip-bottom:before{top:0;margin-top:-12px;margin-left:-6px;border-bottom-color:#fff}.leaflet-tooltip-left{margin-left:-6px}.leaflet-tooltip-right{margin-left:6px}.leaflet-tooltip-left:before,.leaflet-tooltip-right:before{top:50%;margin-top:-6px}.leaflet-tooltip-left:before{right:0;margin-right:-12px;border-left-color:#fff}.leaflet-tooltip-right:before{left:0;margin-left:-12px;border-right-color:#fff}@media print{.leaflet-control{-webkit-print-color-adjust:exact;print-color-adjust:exact}}.leaflet-control-fullscreen a{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAA0CAYAAACU7CiIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACoSURBVFiF7ZZhDoAgCIWxdbF3suxkHM3+1FaOmNqyIr6fiHuJTyKklKgHQxcVF7rCKAUBiA5h5tCSR/T0iTakL9PWz05IZNEM3YSCt6BvCgFI2ps4Q9v3k9Ldgdrr8nrX9LYc7wwu5EIu9KCQT6rq+r8mVbV0ewBEIpqy8MzMsWR/8f+oxmES9u7olZPqLKQeYtqkWuy61V2xND/H3h35pNqMPTPYE1oAnZZStKN8jj8AAAAASUVORK5CYII=) no-repeat 0 0;background-size:26px 52px}.leaflet-touch .leaflet-control-fullscreen a{background-position:2px 2px}.leaflet-fullscreen-on .leaflet-control-fullscreen a{background-position:0 -26px}.leaflet-touch.leaflet-fullscreen-on .leaflet-control-fullscreen a{background-position:2px -24px}.leaflet-container:-webkit-full-screen{width:100%!important;height:100%!important}.leaflet-container.leaflet-fullscreen-on,.leaflet-pseudo-fullscreen{width:100%!important;height:100%!important}.leaflet-pseudo-fullscreen{position:fixed!important;top:0!important;left:0!important;z-index:99999}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.leaflet-control-fullscreen a{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAABoCAYAAAC+NNNnAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAbrwAAG68BXhqRHAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEhSURBVHic7dpBDoIwFADRj/FiPRlwsh4NN5CoiVKg1Ukzb43ApKK1dliWJXpy+/cN1GYQnUF0BtEZRHcvPTCldGhKkXMefnm+TXcjZBBd8TP0rvQ9ffb1R5+xTXcjZBCdQXQG0Q2u+sAZRGcQnUF0p9cUrv4eanW97kbIIDqD6AyiO70ut7du1mrdbU93I2QQnWsKdAbRGURnEJ1BdAbRGURnEJ1BdAbRueeUziA695zSGURnEN3pT7lvUkpTRIw7h80556n2tauPUGFMRMS4HltV9f+HWs3RSnX3DBlEZxCdQXQt9pzOUfbFuh179Xovqo/QOp35eKNPmkx9mszl1hudWpx7T3fPkEF0BtG555TOIDr3nNIZRGcQnUF0BtE9AF5WX48h7QeZAAAAAElFTkSuQmCC)}}.marker-icon{background-color:#fff;border:1px solid #38f;border-radius:50%;margin:-8px 0 0 -8px!important;width:14px!important;height:14px!important;outline:0;transition:opacity .3s ease}.marker-icon-middle{opacity:.7;margin:-6px 0 0 -6px!important;width:10px!important;height:10px!important}.leaflet-pm-draggable{cursor:move!important}.cursor-marker{cursor:crosshair;pointer-events:none;opacity:0}.cursor-marker.visible{opacity:1!important}.geoman-draw-cursor{cursor:crosshair}.rect-start-marker,.rect-style-marker{opacity:0}.rect-start-marker.visible,.rect-style-marker.visible{opacity:1!important}.vertexmarker-disabled{opacity:.7}.pm-text-marker{width:0;height:0}.pm-textarea{background-color:#fff;color:#000;resize:none;border:none;outline:0;cursor:pointer;border-radius:3px;padding-left:7px;padding-bottom:0;padding-top:4px}.leaflet-pm-draggable .pm-textarea{cursor:move}.pm-textarea:active,.pm-textarea:focus,.pm-textarea:focus-visible,.pm-textarea:focus-within{border:2px solid #000;outline:0}.pm-textarea.pm-disabled{border:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pm-textarea.pm-hasfocus{cursor:auto}.leaflet-pm-toolbar .leaflet-buttons-control-button{padding:5px;box-sizing:border-box;position:relative;z-index:3}.leaflet-pm-toolbar .button-container a.leaflet-buttons-control-button,.leaflet-pm-toolbar .leaflet-pm-actions-container a.leaflet-pm-action:first-child:not(.pos-right),.leaflet-pm-toolbar .leaflet-pm-actions-container a.leaflet-pm-action:last-child.pos-right{border-radius:0}.leaflet-pm-toolbar .button-container:last-child a.leaflet-buttons-control-button{border-radius:0 0 2px 2px}.leaflet-pm-toolbar .button-container:first-child a.leaflet-buttons-control-button{border-radius:2px 2px 0 0}.leaflet-pm-toolbar .button-container:last-child a.leaflet-buttons-control-button{border-bottom:none}.leaflet-pm-toolbar .control-fa-icon{font-size:19px;line-height:24px}.leaflet-pm-toolbar .control-icon{width:100%;height:100%;box-sizing:border-box;background-size:contain;background-repeat:no-repeat;background-position:50%}.leaflet-pm-toolbar .leaflet-pm-icon-marker{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M15.5 24.878c-.21 0-.326-.031-.84-.643C10.22 19.412 8 15.501 8 12.505A7.502 7.502 0 0 1 15.5 5c4.142 0 7.5 3.36 7.5 7.504 0 4.496-4.712 9.423-6.666 11.74-.512.606-.625.635-.834.634Zm0-9.345c1.775 0 3.214-1.415 3.214-3.16s-1.439-3.16-3.214-3.16-3.214 1.415-3.214 3.16 1.439 3.16 3.214 3.16Z'/%3E%3C/defs%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero' transform='translate(-3 -3)'/%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-polygon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M19.42 9.165a3.5 3.5 0 1 1 3.58 1.8v8.07A3.5 3.5 0 1 1 19.035 23h-8.07a3.5 3.5 0 1 1-1.8-3.58L19.421 9.166Zm1.415 1.414L10.579 20.835c.03.054.058.11.084.165h8.674A3.514 3.514 0 0 1 21 19.337v-8.674a3.488 3.488 0 0 1-.165-.084ZM22.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm0 15a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm-15 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-3 -3)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-polyline{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='m9.165 19.42 9.256-9.255a3.5 3.5 0 1 1 1.414 1.414l-9.256 9.256a3.5 3.5 0 1 1-1.414-1.414ZM21.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm-14 14a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-3 -3)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-circle{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M18.29 6.786a3.5 3.5 0 0 1 4.924 4.924A9.468 9.468 0 0 1 24 15.5 9.5 9.5 0 1 1 14.5 6c1.347 0 2.629.28 3.79.786Zm-1.14 1.696a7.5 7.5 0 1 0 4.368 4.368 3.5 3.5 0 0 1-4.368-4.368ZM14.5 17a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm6-6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-3 -3)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-circle-marker{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%235B5B5B' stroke-width='8' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='35'/%3E%3Ccircle cx='50' cy='50' r='3' fill='%235B5B5B'/%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-rectangle{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M23 10.965v8.07A3.5 3.5 0 1 1 19.035 23h-8.07A3.5 3.5 0 1 1 7 19.035v-8.07A3.5 3.5 0 1 1 10.965 7h8.07A3.5 3.5 0 1 1 23 10.965Zm-2-.302A3.514 3.514 0 0 1 19.337 9h-8.674A3.514 3.514 0 0 1 9 10.663v8.674A3.514 3.514 0 0 1 10.663 21h8.674A3.514 3.514 0 0 1 21 19.337v-8.674ZM7.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm15 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm0 15a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm-15 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-3 -3)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-delete{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='m17.787 18.481-6.139-5.131-5.243 6.032 4.149 3.606h3.315l3.918-4.507Zm-1.28 4.507H26v2H9.807l-4.714-4.097a2 2 0 0 1-.198-2.822L16.048 5.24a2 2 0 0 1 2.822-.197l6.037 5.249a2 2 0 0 1 .198 2.821l-8.598 9.876Z'/%3E%3C/defs%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero' transform='translate(-3 -3)'/%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-edit{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M13.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7Zm0-2a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM12 7.53a1.5 1.5 0 0 0 .98 1.378L3 15v-2l9-5.47Zm2.217-1.348L19.453 3h3.206l-7.67 4.682a1.5 1.5 0 0 0-.772-1.5Zm9.226 13.103h-3.315l1.745 4.25a.58.58 0 0 1-.295.75l-1.537.67a.553.553 0 0 1-.729-.304l-1.658-4.036-2.708 2.786c-.36.371-.946.085-.946-.402V9.572c0-.513.623-.763.946-.402l8.888 9.142c.359.35.094.973-.39.973Z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-3 -3)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-drag{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M21 14v-4l6 5-6 5v-4h-5v5h4l-5 6-5-6h4v-5H9v4l-6-5 6-5v4h5V9h-4l5-6 5 6h-4v5h5Z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(-3 -3)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%23D8D8D8'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-cut{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='m12.97 13.494 8.062-7.952 2.433.135-6.418 8.834 10.519 2.622-1.777 1.668-11.204-.902-.936 1.289a3.5 3.5 0 1 1-2.215-.354l1.247-1.716-.157-.743-.573-1.074-2.058-.513a3.5 3.5 0 1 1 1.469-1.695l1.607.4Zm-5.212-.269a1.5 1.5 0 1 0 .726-2.91 1.5 1.5 0 0 0-.726 2.91Zm3.045 8.178a1.5 1.5 0 1 0 2.427 1.763 1.5 1.5 0 0 0-2.427-1.763Z'/%3E%3C/defs%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero' transform='rotate(-32 9.362 19.394)'/%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-snapping{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M22 10.943v6.384C22 21.565 18.642 25 14.5 25 10.358 25 7 21.565 7 17.327V10.99l4.002.007-.001 2.157L11 17.327C11 19.376 12.588 21 14.5 21s3.5-1.624 3.5-3.673l-.001-6.336 4-.048ZM10 7a1 1 0 0 1 1 1v2H7V8a1 1 0 0 1 1-1h2Zm11 0a1 1 0 0 1 1 1v2h-4V8a1 1 0 0 1 1-1h2Z'/%3E%3C/defs%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero' transform='rotate(45 16.621 10.879)'/%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-rotate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24'%3E%3Cdefs%3E%3Cpath id='a' d='M21.2 5.8c-.1-.2-.2-.3-.3-.5l-.1-.2c-.1-.2-.2-.3-.3-.5l-.1-.2c-.1-.2-.2-.3-.4-.5l-.2-.3L22.6.5 18 .6l-4.6.1.5 4.5.5 4.5 3.2-3.6v.1l.1.2c.1.1.1.2.2.2l.1.2c0 .2 0 .3.1.4.3.7.6 1.4.7 2.1.2 1.4 0 2.9-.6 4.2l-.2.4-.1.1-.3.5-.1.2c-.2.2-.4.5-.6.7-.5.5-1.1 1-1.7 1.3-.6.4-1.3.6-2.1.8-.7.1-1.5.2-2.2.1-.8-.1-1.5-.3-2.2-.5-.7-.3-1.3-.7-1.9-1.2l-.4-.4-.2-.3L6 15c-.1-.1-.2-.2-.2-.3l-.3-.4-.1-.1-.2-.4c0-.1-.1-.1-.1-.2l-.3-.5-.1-.2-.3-.9c-.2-.8-.3-1.6-.3-2.4v-.7c0-.2 0-.3.1-.4l.1-.6.2-.6c.3-.8.7-1.5 1.2-2.2.5-.7 1.1-1.3 1.8-1.8.2-.1.3-.4.1-.6-.1-.1-.2-.2-.3-.2h-.2l-.1.1c-.9.4-1.6 1-2.3 1.6C4 4.9 3.5 5.7 3 6.6c-.9 1.8-1.2 3.8-.8 5.8.1.5.2.9.3 1.4l.3.8c.1.1.2.2.2.4l.2.4c0 .1.1.2.1.2l.3.5c.1.2.2.3.3.5l.1.2c.1.1.2.3.3.4l.7.6c.7.7 1.6 1.3 2.5 1.8.9.5 1.9.8 3 .9.5.1 1 .1 1.5.1.6 0 1.1 0 1.6-.1 1-.2 2.1-.5 3-1l.2-.1c.2-.1.3-.2.5-.3l.7-.4c.2-.1.3-.2.4-.3l.2-.2c.2-.1.4-.3.5-.5l.1-.1c.3-.3.7-.7.9-1l.6-.9.4-.6c1-1.9 1.4-4.1 1.1-6.2-.2-1.1-.5-2.2-1-3.1z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd' transform='translate(0 2)'%3E%3Cmask id='b' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3C/mask%3E%3Cuse xlink:href='%23a' fill='%235B5B5B' fill-rule='nonzero'/%3E%3Cg fill='%235B5B5B' mask='url(%23b)'%3E%3Cpath d='M0 0h30v30H0z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.leaflet-pm-toolbar .leaflet-pm-icon-text{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19.64 7.27V4H12v16h3.91-7.82H12V4H4.36v3.27' data-name='Ebene 2' style='fill:none;stroke:%235b5b5b;stroke-linecap:round;stroke-linejoin:round;stroke-width:2.5px'/%3E%3C/svg%3E")}.leaflet-buttons-control-button:focus,.leaflet-buttons-control-button:hover{cursor:pointer;background-color:#f4f4f4}.active>.leaflet-buttons-control-button{box-shadow:inset 0 -1px 5px 2px #514d4d4f}.leaflet-buttons-control-text-hide{display:none}.button-container{position:relative}.button-container .leaflet-pm-actions-container{z-index:2;position:absolute;top:0;left:100%;display:none;white-space:nowrap;direction:ltr}.leaflet-right .leaflet-pm-toolbar .button-container .leaflet-pm-actions-container{right:100%;left:auto}.button-container.active .leaflet-pm-actions-container{display:block}.button-container .leaflet-pm-actions-container:not(.pos-right) a.leaflet-pm-action:last-child{border-radius:0 3px 3px 0;border-right:0}.button-container .leaflet-pm-actions-container.pos-right a.leaflet-pm-action:first-child{border-radius:3px 0 0 3px}.button-container .leaflet-pm-actions-container.pos-right a.leaflet-pm-action:last-child{border-right:0}.button-container .leaflet-pm-actions-container .leaflet-pm-action{padding:0 10px;background-color:#666;color:#fff;display:inline-block;width:auto;border-right:1px solid #eee;-webkit-user-select:none;-moz-user-select:none;user-select:none;border-bottom:none;height:29px;line-height:29px;vertical-align:middle}.leaflet-pm-toolbar .button-container:first-child.pos-right.active a.leaflet-buttons-control-button{border-top-left-radius:0}.leaflet-pm-toolbar .button-container:first-child.active:not(.pos-right) a.leaflet-buttons-control-button{border-top-right-radius:0}.button-container .leaflet-pm-actions-container .leaflet-pm-action:focus,.button-container .leaflet-pm-actions-container .leaflet-pm-action:hover{cursor:pointer;background-color:#777}.leaflet-pm-toolbar.activeChild{z-index:801}.leaflet-buttons-control-button.pm-disabled{background-color:#f4f4f4}.leaflet-buttons-control-button.pm-disabled>.control-icon{filter:opacity(.6)}.map-icon{stroke:#dbeafe}.map-location-button{position:absolute;bottom:20px;right:20px;width:40px;height:40px;background-color:#eee!important;border-radius:50%;border:none;box-shadow:0 2px 4px #0003;cursor:pointer;display:flex;justify-content:center;align-items:center;z-index:23332;color:#c83a3a}.map-location-button svg{width:24px;height:24px;fill:#333} -------------------------------------------------------------------------------- /resources/dist/filament-map-picker.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../node_modules/leaflet/dist/leaflet.css","../../node_modules/leaflet-fullscreen/dist/leaflet.fullscreen.css","../../node_modules/@geoman-io/leaflet-geoman-free/src/css/layers.css","../../node_modules/@geoman-io/leaflet-geoman-free/src/css/controls.css","../css/index.css"],"names":[],"mappings":"AAEA,6LAUC,iBAAkB,CAClB,MAAO,CACP,KACA,CACD,mBACC,eACA,CACD,0DAGC,wBAAyB,CACtB,qBAAsB,CACjB,gBAAiB,CACvB,sBACF,CAED,8BACC,gBACD,CAFA,yBACC,gBACD,CAEA,8BACC,yCACA,CAED,wCACC,YAAa,CACb,aAAc,CACd,4BACA,CACD,4CAEC,aACA,CAGD,6CACC,wBAA0B,CAC1B,yBACA,CACD,8MAKC,wBAA0B,CAC1B,yBAA2B,CAC3B,UAAW,CACX,SACA,CAED,oCAEC,2BACD,CAEA,sCAEC,wBACA,CACD,sCAGC,iBAAkB,CAClB,uBACD,CACA,yDAEC,iBACD,CACA,mBACC,uCACD,CACA,qBACC,+CACD,CACA,cACC,cAAe,CACf,iBACA,CACD,qBACC,kBACA,CACD,kBACC,OAAQ,CACR,QAAS,CAEJ,qBAAsB,CAC3B,WACA,CAED,0BACC,qBACA,CAED,cAAwB,WAAc,CAEtC,mBAAwB,WAAc,CACtC,sBAAwB,WAAc,CACtC,qBAAwB,WAAc,CACtC,qBAAwB,WAAc,CACtC,sBAA0B,WAAc,CACxC,oBAAwB,WAAc,CAEtC,yBAA2B,WAAc,CACzC,sBAA2B,WAAc,CAEzC,mBACC,SAAU,CACV,UACA,CACD,MACC,0BAA2B,CAC3B,oBAAqB,CACrB,iBACA,CAKD,iBACC,iBAAkB,CAClB,WAAY,CACZ,6BAA8B,CAC9B,mBACA,CACD,6BAEC,iBAAkB,CAClB,YAAa,CACb,mBACA,CACD,aACC,KACA,CACD,eACC,OACA,CACD,gBACC,QACA,CACD,cACC,MACA,CACD,iBACC,UAAW,CACX,UACA,CACD,gCACC,WACA,CACD,8BACC,eACA,CACD,iCACC,kBACA,CACD,+BACC,gBACA,CACD,gCACC,iBACA,CAKD,kCACC,SAAU,CAGF,6BACR,CACD,oDACC,SACA,CACD,uBAGS,oBACR,CACD,0BACC,qBACD,CAEA,0CAGS,iDACR,CACD,iEAIS,eACR,CAED,sCACC,iBACA,CAKD,qBACC,cACA,CACD,cAGC,WACA,CACD,2DAEC,gBACA,CACD,qCAEC,WACA,CACD,iIAGC,WAAY,CAGZ,eACA,CAGD,gHAKC,mBACA,CAED,8KAIC,6BAA8B,CAC9B,mBACA,CAID,mBACC,eAAgB,CAChB,kBACA,CACD,qBACC,aACA,CACD,kBACC,sBAAuB,CACvB,oBACA,CAID,mBACC,qDAA2D,CAC3D,cAAe,CACf,gBAAkB,CAClB,eACA,CAKD,aACC,8BAAsC,CACtC,iBACA,CACD,eACC,qBAAsB,CACtB,4BAA6B,CAC7B,UAAW,CACX,WAAY,CACZ,gBAAiB,CACjB,aAAc,CACd,iBAAkB,CAClB,oBAAqB,CACrB,UACA,CACD,8CAEC,2BAA4B,CAC5B,2BAA4B,CAC5B,aACA,CACD,0CAEC,wBACA,CACD,2BACC,0BAA2B,CAC3B,2BACA,CACD,0BACC,6BAA8B,CAC9B,8BAA+B,CAC/B,kBACA,CACD,gCACC,cAAe,CACf,wBAAyB,CACzB,UACA,CAED,8BACC,UAAW,CACX,WAAY,CACZ,gBACA,CACD,0CACC,0BAA2B,CAC3B,2BACA,CACD,yCACC,6BAA8B,CAC9B,8BACA,CAID,mDAEC,6CAAmD,CACnD,eACA,CAED,iFACC,cACA,CAKD,wBACC,0BAAqC,CACrC,eAAgB,CAChB,iBACA,CACD,+BACC,48BAAwC,CACxC,UAAW,CACX,WACA,CACD,+CACC,4rDAA2C,CAC3C,yBACA,CACD,8CACC,UAAW,CACX,WACA,CACD,qHAEC,YACA,CACD,8DACC,aAAc,CACd,iBACA,CACD,iCACC,wBAAyB,CACzB,UAAW,CACX,eACA,CACD,kCACC,iBAAkB,CAClB,iBAAkB,CAClB,iBACA,CACD,iCACC,cAAe,CACf,iBAAkB,CAClB,OACA,CACD,8BACC,aAAc,CACd,cAAe,CACf,mBACA,CACD,kCACC,QAAS,CACT,yBAA0B,CAC1B,yBACA,CAGD,2BACC,g9DACA,CAKD,gDACC,eAAgB,CAChB,gBAAoC,CACpC,QACA,CACD,yDAEC,aAAc,CACd,UAAW,CACX,eACA,CACD,+BACC,oBACA,CACD,0EAEC,yBACA,CACD,0BACC,wBAA0B,CAC1B,gCAAmC,CACnC,SAAU,CACV,cACA,CACD,qCACC,eACA,CACD,uCACC,iBACA,CACD,4BAEC,qBAAgB,CAAhB,eAAgB,CAChB,eAAgB,CAChB,mBAAoB,CACpB,kBAAmB,CAEd,qBAAsB,CAC3B,gBAAoC,CACpC,wBACA,CACD,8CACC,yBAA0B,CAC1B,kBAAmB,CACnB,eACA,CACD,+DACC,4BACA,CAED,+GAGC,eACA,CACD,mEAEC,sBAAiC,CACjC,2BACA,CAKD,eACC,iBAAkB,CAClB,iBAAkB,CAClB,kBACA,CACD,+BACC,WAAY,CACZ,eAAgB,CAChB,kBACA,CACD,uBACC,0BAA2B,CAC3B,eAAgB,CAChB,cAAe,CACf,mBAAoB,CACpB,cACA,CACD,yBAEC,cACA,CACD,6BACC,UAAW,CACX,WAAY,CACZ,iBAAkB,CAClB,QAAS,CACT,eAAgB,CAChB,iBAAkB,CAClB,eAAgB,CAChB,mBACA,CACD,mBACC,UAAW,CACX,WAAY,CACZ,WAAY,CAEZ,mBAAoB,CACpB,mBAAoB,CAKZ,uBACR,CACD,kDAEC,eAAiB,CACjB,UAAW,CACX,2BACA,CACD,gDACC,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,WAAY,CACZ,iBAAkB,CAClB,UAAW,CACX,WAAY,CACZ,wCAA2C,CAC3C,aAAc,CACd,oBAAqB,CACrB,gBACA,CACD,4GAEC,aACA,CACD,wBACC,aACA,CAED,8CACC,UACA,CACD,kCACC,UAAW,CACX,aAAc,CAEd,sHAAuH,CACvH,6GACA,CAED,4JAIC,qBACA,CAKD,kBACC,eAAgB,CAChB,qBACA,CAKD,iBACC,iBAAkB,CAClB,WAAY,CACZ,qBAAsB,CACtB,qBAAsB,CACtB,iBAAkB,CAClB,UAAW,CACX,kBAAmB,CACnB,wBAAyB,CACzB,qBAAsB,CAEtB,gBAAiB,CACjB,mBAAoB,CACpB,0BACA,CACD,qCACC,cAAe,CACf,mBACA,CACD,sHAIC,iBAAkB,CAClB,mBAAoB,CACpB,sBAA6B,CAC7B,gBAAuB,CACvB,UACA,CAID,wBACC,cACD,CACA,qBACC,eACD,CACA,2DAEC,QAAS,CACT,gBACA,CACD,4BACC,QAAS,CACT,mBAAoB,CACpB,qBACA,CACD,+BACC,KAAM,CACN,gBAAiB,CACjB,gBAAiB,CACjB,wBACA,CACD,sBACC,gBACD,CACA,uBACC,eACD,CACA,2DAEC,OAAQ,CACR,eACA,CACD,6BACC,OAAQ,CACR,kBAAmB,CACnB,sBACA,CACD,8BACC,MAAO,CACP,iBAAkB,CAClB,uBACA,CAID,aAEC,iBACC,gCAAiC,CACjC,wBACA,CACD,CCppBD,8BACE,ycAAiD,CACjD,yBACA,CACA,6CACE,2BACA,CACF,qDACE,2BACA,CACF,mEACE,6BACA,CAGJ,uCACE,oBAAoB,CACpB,qBACA,CAMF,oEAJE,oBAAoB,CACpB,qBAUA,CAPF,2BACE,wBAAwB,CAGxB,eAAe,CACf,gBAAgB,CAChB,aACA,CAEF,kEAGI,8BACE,4lBACF,CACF,CCvCF,aACE,qBAAkB,CAClB,qBAAkB,CAFpB,iBAGiB,CAHjB,8BAImB,CACjB,oBAAO,CACP,qBAAQ,CACR,SAAS,CACT,2BACF,CAEA,oBACE,UAAS,CAZX,8BAamB,CACjB,oBAAO,CACP,qBACF,CAEA,sBACE,qBACF,CAEA,eACE,gBAAQ,CACR,mBAAgB,CAChB,SACF,CAEA,uBACE,mBACF,CAEA,oBACE,gBACF,CAEA,sCAEE,SACF,CAEA,sDAEE,mBACF,CAEA,uBACE,UACF,CAEA,gBACE,OAAO,CACP,QACF,CAEA,aACE,qBAAkB,CAClB,UAAO,CACP,WAAQ,CACR,WAAQ,CACR,SAAS,CACT,cAAQ,CA7DV,iBA8DiB,CACf,gBAAc,CACd,gBAAgB,CAChB,eACF,CAEA,mCACE,WACF,CAEA,4FAIE,qBAAkB,CAClB,SACF,CAEA,yBACE,WAAQ,CACR,wBAAa,CAAb,qBAAa,CAAb,gBACF,CAEA,yBACE,WACF,CCpFA,oDAHA,WAIW,CACT,qBAAY,CACZ,iBAAU,CACV,SACF,CAWA,oQAnBA,eAqBA,CAEA,kFAvBA,yBA2BA,CAEA,mFA7BA,yBAiCA,CAEA,kFAGE,kBACF,CAEA,qCACE,cAAW,CACX,gBACF,CAEA,kCACE,UAAO,CACP,WAAQ,CACR,qBAAY,CACZ,uBAAiB,CACjB,2BAAmB,CACnB,uBACF,CAEA,4CACE,4lBACF,CACA,6CACE,i2BACF,CACA,8CACE,irBACF,CACA,4CACE,qwBACF,CACA,mDACE,6QACF,CACA,+CACE,w7BACF,CACA,4CACE,ygBACF,CACA,0CACE,m5BACF,CACA,0CACE,yjBACF,CACA,yCACE,woBACF,CACA,8CACE,ykBACF,CACA,4CACE,u6CACF,CACA,0CACE,oTAAkB,CACpB,4EAG+B,cACrB,CAAA,wBACU,CACpB,wCAhGqB,yCAkGc,CACnC,mCAEC,YACU,CACX,kBAvFqB,iBA0FT,CACZ,gDAnGG,SAsGQ,CAAA,iBACC,CAAA,KACL,CAAA,SACC,CAAA,YACG,CAAA,kBACI,CAAA,aACF,CACb,mFA7GG,UAmHM,CAAA,SACD,CACR,uDArHG,aAwHQ,CACX,+FAIqB,yBACM,CAAA,cACX,CAChB,0FAGqB,yBACI,CACzB,yFAGqB,cACL,CAChB,mEAzII,cA2IS,CAAA,qBACO,CAAA,UACX,CAAA,oBACE,CAAA,UACF,CAAA,2BACiB,CAAA,wBACX,CADW,qBACX,CADW,gBACX,CAAA,kBACE,CAAA,WACP,CAAA,gBACK,CAAA,qBACG,CAClB,oGA/JqB,wBAmKK,CAC1B,0GApKqB,yBAwKM,CAC3B,kJAGkE,cACxD,CAAA,qBACU,CACpB,gCAGoB,WACT,CACX,4CAEgC,wBACZ,CACpB,0DA7IqB,kBAgJH,CC1LlB,UACI,cACJ,CAEA,qBACI,iBAAkB,CAClB,WAAY,CACZ,UAAW,CACX,UAAW,CACX,WAAY,CACZ,+BAAiC,CACjC,iBAAkB,CAClB,WAAY,CACZ,0BAA0C,CAC1C,cAAe,CACf,YAAa,CACb,sBAAuB,CACvB,kBAAmB,CACnB,aAAc,CACd,aACJ,CAEA,yBACI,UAAW,CACX,WAAY,CACZ,SACJ","file":"filament-map-picker.css","sourcesContent":["/* required styles */\r\n\r\n.leaflet-pane,\r\n.leaflet-tile,\r\n.leaflet-marker-icon,\r\n.leaflet-marker-shadow,\r\n.leaflet-tile-container,\r\n.leaflet-pane > svg,\r\n.leaflet-pane > canvas,\r\n.leaflet-zoom-box,\r\n.leaflet-image-layer,\r\n.leaflet-layer {\r\n\tposition: absolute;\r\n\tleft: 0;\r\n\ttop: 0;\r\n\t}\r\n.leaflet-container {\r\n\toverflow: hidden;\r\n\t}\r\n.leaflet-tile,\r\n.leaflet-marker-icon,\r\n.leaflet-marker-shadow {\r\n\t-webkit-user-select: none;\r\n\t -moz-user-select: none;\r\n\t user-select: none;\r\n\t -webkit-user-drag: none;\r\n\t}\r\n/* Prevents IE11 from highlighting tiles in blue */\r\n.leaflet-tile::selection {\r\n\tbackground: transparent;\r\n}\r\n/* Safari renders non-retina tile on retina better with this, but Chrome is worse */\r\n.leaflet-safari .leaflet-tile {\r\n\timage-rendering: -webkit-optimize-contrast;\r\n\t}\r\n/* hack that prevents hw layers \"stretching\" when loading new tiles */\r\n.leaflet-safari .leaflet-tile-container {\r\n\twidth: 1600px;\r\n\theight: 1600px;\r\n\t-webkit-transform-origin: 0 0;\r\n\t}\r\n.leaflet-marker-icon,\r\n.leaflet-marker-shadow {\r\n\tdisplay: block;\r\n\t}\r\n/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */\r\n/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */\r\n.leaflet-container .leaflet-overlay-pane svg {\r\n\tmax-width: none !important;\r\n\tmax-height: none !important;\r\n\t}\r\n.leaflet-container .leaflet-marker-pane img,\r\n.leaflet-container .leaflet-shadow-pane img,\r\n.leaflet-container .leaflet-tile-pane img,\r\n.leaflet-container img.leaflet-image-layer,\r\n.leaflet-container .leaflet-tile {\r\n\tmax-width: none !important;\r\n\tmax-height: none !important;\r\n\twidth: auto;\r\n\tpadding: 0;\r\n\t}\r\n\r\n.leaflet-container img.leaflet-tile {\r\n\t/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */\r\n\tmix-blend-mode: plus-lighter;\r\n}\r\n\r\n.leaflet-container.leaflet-touch-zoom {\r\n\t-ms-touch-action: pan-x pan-y;\r\n\ttouch-action: pan-x pan-y;\r\n\t}\r\n.leaflet-container.leaflet-touch-drag {\r\n\t-ms-touch-action: pinch-zoom;\r\n\t/* Fallback for FF which doesn't support pinch-zoom */\r\n\ttouch-action: none;\r\n\ttouch-action: pinch-zoom;\r\n}\r\n.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {\r\n\t-ms-touch-action: none;\r\n\ttouch-action: none;\r\n}\r\n.leaflet-container {\r\n\t-webkit-tap-highlight-color: transparent;\r\n}\r\n.leaflet-container a {\r\n\t-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);\r\n}\r\n.leaflet-tile {\r\n\tfilter: inherit;\r\n\tvisibility: hidden;\r\n\t}\r\n.leaflet-tile-loaded {\r\n\tvisibility: inherit;\r\n\t}\r\n.leaflet-zoom-box {\r\n\twidth: 0;\r\n\theight: 0;\r\n\t-moz-box-sizing: border-box;\r\n\t box-sizing: border-box;\r\n\tz-index: 800;\r\n\t}\r\n/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */\r\n.leaflet-overlay-pane svg {\r\n\t-moz-user-select: none;\r\n\t}\r\n\r\n.leaflet-pane { z-index: 400; }\r\n\r\n.leaflet-tile-pane { z-index: 200; }\r\n.leaflet-overlay-pane { z-index: 400; }\r\n.leaflet-shadow-pane { z-index: 500; }\r\n.leaflet-marker-pane { z-index: 600; }\r\n.leaflet-tooltip-pane { z-index: 650; }\r\n.leaflet-popup-pane { z-index: 700; }\r\n\r\n.leaflet-map-pane canvas { z-index: 100; }\r\n.leaflet-map-pane svg { z-index: 200; }\r\n\r\n.leaflet-vml-shape {\r\n\twidth: 1px;\r\n\theight: 1px;\r\n\t}\r\n.lvml {\r\n\tbehavior: url(#default#VML);\r\n\tdisplay: inline-block;\r\n\tposition: absolute;\r\n\t}\r\n\r\n\r\n/* control positioning */\r\n\r\n.leaflet-control {\r\n\tposition: relative;\r\n\tz-index: 800;\r\n\tpointer-events: visiblePainted; /* IE 9-10 doesn't have auto */\r\n\tpointer-events: auto;\r\n\t}\r\n.leaflet-top,\r\n.leaflet-bottom {\r\n\tposition: absolute;\r\n\tz-index: 1000;\r\n\tpointer-events: none;\r\n\t}\r\n.leaflet-top {\r\n\ttop: 0;\r\n\t}\r\n.leaflet-right {\r\n\tright: 0;\r\n\t}\r\n.leaflet-bottom {\r\n\tbottom: 0;\r\n\t}\r\n.leaflet-left {\r\n\tleft: 0;\r\n\t}\r\n.leaflet-control {\r\n\tfloat: left;\r\n\tclear: both;\r\n\t}\r\n.leaflet-right .leaflet-control {\r\n\tfloat: right;\r\n\t}\r\n.leaflet-top .leaflet-control {\r\n\tmargin-top: 10px;\r\n\t}\r\n.leaflet-bottom .leaflet-control {\r\n\tmargin-bottom: 10px;\r\n\t}\r\n.leaflet-left .leaflet-control {\r\n\tmargin-left: 10px;\r\n\t}\r\n.leaflet-right .leaflet-control {\r\n\tmargin-right: 10px;\r\n\t}\r\n\r\n\r\n/* zoom and fade animations */\r\n\r\n.leaflet-fade-anim .leaflet-popup {\r\n\topacity: 0;\r\n\t-webkit-transition: opacity 0.2s linear;\r\n\t -moz-transition: opacity 0.2s linear;\r\n\t transition: opacity 0.2s linear;\r\n\t}\r\n.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {\r\n\topacity: 1;\r\n\t}\r\n.leaflet-zoom-animated {\r\n\t-webkit-transform-origin: 0 0;\r\n\t -ms-transform-origin: 0 0;\r\n\t transform-origin: 0 0;\r\n\t}\r\nsvg.leaflet-zoom-animated {\r\n\twill-change: transform;\r\n}\r\n\r\n.leaflet-zoom-anim .leaflet-zoom-animated {\r\n\t-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);\r\n\t -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);\r\n\t transition: transform 0.25s cubic-bezier(0,0,0.25,1);\r\n\t}\r\n.leaflet-zoom-anim .leaflet-tile,\r\n.leaflet-pan-anim .leaflet-tile {\r\n\t-webkit-transition: none;\r\n\t -moz-transition: none;\r\n\t transition: none;\r\n\t}\r\n\r\n.leaflet-zoom-anim .leaflet-zoom-hide {\r\n\tvisibility: hidden;\r\n\t}\r\n\r\n\r\n/* cursors */\r\n\r\n.leaflet-interactive {\r\n\tcursor: pointer;\r\n\t}\r\n.leaflet-grab {\r\n\tcursor: -webkit-grab;\r\n\tcursor: -moz-grab;\r\n\tcursor: grab;\r\n\t}\r\n.leaflet-crosshair,\r\n.leaflet-crosshair .leaflet-interactive {\r\n\tcursor: crosshair;\r\n\t}\r\n.leaflet-popup-pane,\r\n.leaflet-control {\r\n\tcursor: auto;\r\n\t}\r\n.leaflet-dragging .leaflet-grab,\r\n.leaflet-dragging .leaflet-grab .leaflet-interactive,\r\n.leaflet-dragging .leaflet-marker-draggable {\r\n\tcursor: move;\r\n\tcursor: -webkit-grabbing;\r\n\tcursor: -moz-grabbing;\r\n\tcursor: grabbing;\r\n\t}\r\n\r\n/* marker & overlays interactivity */\r\n.leaflet-marker-icon,\r\n.leaflet-marker-shadow,\r\n.leaflet-image-layer,\r\n.leaflet-pane > svg path,\r\n.leaflet-tile-container {\r\n\tpointer-events: none;\r\n\t}\r\n\r\n.leaflet-marker-icon.leaflet-interactive,\r\n.leaflet-image-layer.leaflet-interactive,\r\n.leaflet-pane > svg path.leaflet-interactive,\r\nsvg.leaflet-image-layer.leaflet-interactive path {\r\n\tpointer-events: visiblePainted; /* IE 9-10 doesn't have auto */\r\n\tpointer-events: auto;\r\n\t}\r\n\r\n/* visual tweaks */\r\n\r\n.leaflet-container {\r\n\tbackground: #ddd;\r\n\toutline-offset: 1px;\r\n\t}\r\n.leaflet-container a {\r\n\tcolor: #0078A8;\r\n\t}\r\n.leaflet-zoom-box {\r\n\tborder: 2px dotted #38f;\r\n\tbackground: rgba(255,255,255,0.5);\r\n\t}\r\n\r\n\r\n/* general typography */\r\n.leaflet-container {\r\n\tfont-family: \"Helvetica Neue\", Arial, Helvetica, sans-serif;\r\n\tfont-size: 12px;\r\n\tfont-size: 0.75rem;\r\n\tline-height: 1.5;\r\n\t}\r\n\r\n\r\n/* general toolbar styles */\r\n\r\n.leaflet-bar {\r\n\tbox-shadow: 0 1px 5px rgba(0,0,0,0.65);\r\n\tborder-radius: 4px;\r\n\t}\r\n.leaflet-bar a {\r\n\tbackground-color: #fff;\r\n\tborder-bottom: 1px solid #ccc;\r\n\twidth: 26px;\r\n\theight: 26px;\r\n\tline-height: 26px;\r\n\tdisplay: block;\r\n\ttext-align: center;\r\n\ttext-decoration: none;\r\n\tcolor: black;\r\n\t}\r\n.leaflet-bar a,\r\n.leaflet-control-layers-toggle {\r\n\tbackground-position: 50% 50%;\r\n\tbackground-repeat: no-repeat;\r\n\tdisplay: block;\r\n\t}\r\n.leaflet-bar a:hover,\r\n.leaflet-bar a:focus {\r\n\tbackground-color: #f4f4f4;\r\n\t}\r\n.leaflet-bar a:first-child {\r\n\tborder-top-left-radius: 4px;\r\n\tborder-top-right-radius: 4px;\r\n\t}\r\n.leaflet-bar a:last-child {\r\n\tborder-bottom-left-radius: 4px;\r\n\tborder-bottom-right-radius: 4px;\r\n\tborder-bottom: none;\r\n\t}\r\n.leaflet-bar a.leaflet-disabled {\r\n\tcursor: default;\r\n\tbackground-color: #f4f4f4;\r\n\tcolor: #bbb;\r\n\t}\r\n\r\n.leaflet-touch .leaflet-bar a {\r\n\twidth: 30px;\r\n\theight: 30px;\r\n\tline-height: 30px;\r\n\t}\r\n.leaflet-touch .leaflet-bar a:first-child {\r\n\tborder-top-left-radius: 2px;\r\n\tborder-top-right-radius: 2px;\r\n\t}\r\n.leaflet-touch .leaflet-bar a:last-child {\r\n\tborder-bottom-left-radius: 2px;\r\n\tborder-bottom-right-radius: 2px;\r\n\t}\r\n\r\n/* zoom control */\r\n\r\n.leaflet-control-zoom-in,\r\n.leaflet-control-zoom-out {\r\n\tfont: bold 18px 'Lucida Console', Monaco, monospace;\r\n\ttext-indent: 1px;\r\n\t}\r\n\r\n.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {\r\n\tfont-size: 22px;\r\n\t}\r\n\r\n\r\n/* layers control */\r\n\r\n.leaflet-control-layers {\r\n\tbox-shadow: 0 1px 5px rgba(0,0,0,0.4);\r\n\tbackground: #fff;\r\n\tborder-radius: 5px;\r\n\t}\r\n.leaflet-control-layers-toggle {\r\n\tbackground-image: url(images/layers.png);\r\n\twidth: 36px;\r\n\theight: 36px;\r\n\t}\r\n.leaflet-retina .leaflet-control-layers-toggle {\r\n\tbackground-image: url(images/layers-2x.png);\r\n\tbackground-size: 26px 26px;\r\n\t}\r\n.leaflet-touch .leaflet-control-layers-toggle {\r\n\twidth: 44px;\r\n\theight: 44px;\r\n\t}\r\n.leaflet-control-layers .leaflet-control-layers-list,\r\n.leaflet-control-layers-expanded .leaflet-control-layers-toggle {\r\n\tdisplay: none;\r\n\t}\r\n.leaflet-control-layers-expanded .leaflet-control-layers-list {\r\n\tdisplay: block;\r\n\tposition: relative;\r\n\t}\r\n.leaflet-control-layers-expanded {\r\n\tpadding: 6px 10px 6px 6px;\r\n\tcolor: #333;\r\n\tbackground: #fff;\r\n\t}\r\n.leaflet-control-layers-scrollbar {\r\n\toverflow-y: scroll;\r\n\toverflow-x: hidden;\r\n\tpadding-right: 5px;\r\n\t}\r\n.leaflet-control-layers-selector {\r\n\tmargin-top: 2px;\r\n\tposition: relative;\r\n\ttop: 1px;\r\n\t}\r\n.leaflet-control-layers label {\r\n\tdisplay: block;\r\n\tfont-size: 13px;\r\n\tfont-size: 1.08333em;\r\n\t}\r\n.leaflet-control-layers-separator {\r\n\theight: 0;\r\n\tborder-top: 1px solid #ddd;\r\n\tmargin: 5px -10px 5px -6px;\r\n\t}\r\n\r\n/* Default icon URLs */\r\n.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */\r\n\tbackground-image: url(images/marker-icon.png);\r\n\t}\r\n\r\n\r\n/* attribution and scale controls */\r\n\r\n.leaflet-container .leaflet-control-attribution {\r\n\tbackground: #fff;\r\n\tbackground: rgba(255, 255, 255, 0.8);\r\n\tmargin: 0;\r\n\t}\r\n.leaflet-control-attribution,\r\n.leaflet-control-scale-line {\r\n\tpadding: 0 5px;\r\n\tcolor: #333;\r\n\tline-height: 1.4;\r\n\t}\r\n.leaflet-control-attribution a {\r\n\ttext-decoration: none;\r\n\t}\r\n.leaflet-control-attribution a:hover,\r\n.leaflet-control-attribution a:focus {\r\n\ttext-decoration: underline;\r\n\t}\r\n.leaflet-attribution-flag {\r\n\tdisplay: inline !important;\r\n\tvertical-align: baseline !important;\r\n\twidth: 1em;\r\n\theight: 0.6669em;\r\n\t}\r\n.leaflet-left .leaflet-control-scale {\r\n\tmargin-left: 5px;\r\n\t}\r\n.leaflet-bottom .leaflet-control-scale {\r\n\tmargin-bottom: 5px;\r\n\t}\r\n.leaflet-control-scale-line {\r\n\tborder: 2px solid #777;\r\n\tborder-top: none;\r\n\tline-height: 1.1;\r\n\tpadding: 2px 5px 1px;\r\n\twhite-space: nowrap;\r\n\t-moz-box-sizing: border-box;\r\n\t box-sizing: border-box;\r\n\tbackground: rgba(255, 255, 255, 0.8);\r\n\ttext-shadow: 1px 1px #fff;\r\n\t}\r\n.leaflet-control-scale-line:not(:first-child) {\r\n\tborder-top: 2px solid #777;\r\n\tborder-bottom: none;\r\n\tmargin-top: -2px;\r\n\t}\r\n.leaflet-control-scale-line:not(:first-child):not(:last-child) {\r\n\tborder-bottom: 2px solid #777;\r\n\t}\r\n\r\n.leaflet-touch .leaflet-control-attribution,\r\n.leaflet-touch .leaflet-control-layers,\r\n.leaflet-touch .leaflet-bar {\r\n\tbox-shadow: none;\r\n\t}\r\n.leaflet-touch .leaflet-control-layers,\r\n.leaflet-touch .leaflet-bar {\r\n\tborder: 2px solid rgba(0,0,0,0.2);\r\n\tbackground-clip: padding-box;\r\n\t}\r\n\r\n\r\n/* popup */\r\n\r\n.leaflet-popup {\r\n\tposition: absolute;\r\n\ttext-align: center;\r\n\tmargin-bottom: 20px;\r\n\t}\r\n.leaflet-popup-content-wrapper {\r\n\tpadding: 1px;\r\n\ttext-align: left;\r\n\tborder-radius: 12px;\r\n\t}\r\n.leaflet-popup-content {\r\n\tmargin: 13px 24px 13px 20px;\r\n\tline-height: 1.3;\r\n\tfont-size: 13px;\r\n\tfont-size: 1.08333em;\r\n\tmin-height: 1px;\r\n\t}\r\n.leaflet-popup-content p {\r\n\tmargin: 17px 0;\r\n\tmargin: 1.3em 0;\r\n\t}\r\n.leaflet-popup-tip-container {\r\n\twidth: 40px;\r\n\theight: 20px;\r\n\tposition: absolute;\r\n\tleft: 50%;\r\n\tmargin-top: -1px;\r\n\tmargin-left: -20px;\r\n\toverflow: hidden;\r\n\tpointer-events: none;\r\n\t}\r\n.leaflet-popup-tip {\r\n\twidth: 17px;\r\n\theight: 17px;\r\n\tpadding: 1px;\r\n\r\n\tmargin: -10px auto 0;\r\n\tpointer-events: auto;\r\n\r\n\t-webkit-transform: rotate(45deg);\r\n\t -moz-transform: rotate(45deg);\r\n\t -ms-transform: rotate(45deg);\r\n\t transform: rotate(45deg);\r\n\t}\r\n.leaflet-popup-content-wrapper,\r\n.leaflet-popup-tip {\r\n\tbackground: white;\r\n\tcolor: #333;\r\n\tbox-shadow: 0 3px 14px rgba(0,0,0,0.4);\r\n\t}\r\n.leaflet-container a.leaflet-popup-close-button {\r\n\tposition: absolute;\r\n\ttop: 0;\r\n\tright: 0;\r\n\tborder: none;\r\n\ttext-align: center;\r\n\twidth: 24px;\r\n\theight: 24px;\r\n\tfont: 16px/24px Tahoma, Verdana, sans-serif;\r\n\tcolor: #757575;\r\n\ttext-decoration: none;\r\n\tbackground: transparent;\r\n\t}\r\n.leaflet-container a.leaflet-popup-close-button:hover,\r\n.leaflet-container a.leaflet-popup-close-button:focus {\r\n\tcolor: #585858;\r\n\t}\r\n.leaflet-popup-scrolled {\r\n\toverflow: auto;\r\n\t}\r\n\r\n.leaflet-oldie .leaflet-popup-content-wrapper {\r\n\t-ms-zoom: 1;\r\n\t}\r\n.leaflet-oldie .leaflet-popup-tip {\r\n\twidth: 24px;\r\n\tmargin: 0 auto;\r\n\r\n\t-ms-filter: \"progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)\";\r\n\tfilter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);\r\n\t}\r\n\r\n.leaflet-oldie .leaflet-control-zoom,\r\n.leaflet-oldie .leaflet-control-layers,\r\n.leaflet-oldie .leaflet-popup-content-wrapper,\r\n.leaflet-oldie .leaflet-popup-tip {\r\n\tborder: 1px solid #999;\r\n\t}\r\n\r\n\r\n/* div icon */\r\n\r\n.leaflet-div-icon {\r\n\tbackground: #fff;\r\n\tborder: 1px solid #666;\r\n\t}\r\n\r\n\r\n/* Tooltip */\r\n/* Base styles for the element that has a tooltip */\r\n.leaflet-tooltip {\r\n\tposition: absolute;\r\n\tpadding: 6px;\r\n\tbackground-color: #fff;\r\n\tborder: 1px solid #fff;\r\n\tborder-radius: 3px;\r\n\tcolor: #222;\r\n\twhite-space: nowrap;\r\n\t-webkit-user-select: none;\r\n\t-moz-user-select: none;\r\n\t-ms-user-select: none;\r\n\tuser-select: none;\r\n\tpointer-events: none;\r\n\tbox-shadow: 0 1px 3px rgba(0,0,0,0.4);\r\n\t}\r\n.leaflet-tooltip.leaflet-interactive {\r\n\tcursor: pointer;\r\n\tpointer-events: auto;\r\n\t}\r\n.leaflet-tooltip-top:before,\r\n.leaflet-tooltip-bottom:before,\r\n.leaflet-tooltip-left:before,\r\n.leaflet-tooltip-right:before {\r\n\tposition: absolute;\r\n\tpointer-events: none;\r\n\tborder: 6px solid transparent;\r\n\tbackground: transparent;\r\n\tcontent: \"\";\r\n\t}\r\n\r\n/* Directions */\r\n\r\n.leaflet-tooltip-bottom {\r\n\tmargin-top: 6px;\r\n}\r\n.leaflet-tooltip-top {\r\n\tmargin-top: -6px;\r\n}\r\n.leaflet-tooltip-bottom:before,\r\n.leaflet-tooltip-top:before {\r\n\tleft: 50%;\r\n\tmargin-left: -6px;\r\n\t}\r\n.leaflet-tooltip-top:before {\r\n\tbottom: 0;\r\n\tmargin-bottom: -12px;\r\n\tborder-top-color: #fff;\r\n\t}\r\n.leaflet-tooltip-bottom:before {\r\n\ttop: 0;\r\n\tmargin-top: -12px;\r\n\tmargin-left: -6px;\r\n\tborder-bottom-color: #fff;\r\n\t}\r\n.leaflet-tooltip-left {\r\n\tmargin-left: -6px;\r\n}\r\n.leaflet-tooltip-right {\r\n\tmargin-left: 6px;\r\n}\r\n.leaflet-tooltip-left:before,\r\n.leaflet-tooltip-right:before {\r\n\ttop: 50%;\r\n\tmargin-top: -6px;\r\n\t}\r\n.leaflet-tooltip-left:before {\r\n\tright: 0;\r\n\tmargin-right: -12px;\r\n\tborder-left-color: #fff;\r\n\t}\r\n.leaflet-tooltip-right:before {\r\n\tleft: 0;\r\n\tmargin-left: -12px;\r\n\tborder-right-color: #fff;\r\n\t}\r\n\r\n/* Printing */\r\n\r\n@media print {\r\n\t/* Prevent printers from removing background-images of controls. */\r\n\t.leaflet-control {\r\n\t\t-webkit-print-color-adjust: exact;\r\n\t\tprint-color-adjust: exact;\r\n\t\t}\r\n\t}\r\n",".leaflet-control-fullscreen a {\n background:#fff url(fullscreen.png) no-repeat 0 0;\n background-size:26px 52px;\n }\n .leaflet-touch .leaflet-control-fullscreen a {\n background-position: 2px 2px;\n }\n .leaflet-fullscreen-on .leaflet-control-fullscreen a {\n background-position:0 -26px;\n }\n .leaflet-touch.leaflet-fullscreen-on .leaflet-control-fullscreen a {\n background-position: 2px -24px;\n }\n\n/* Do not combine these two rules; IE will break. */\n.leaflet-container:-webkit-full-screen {\n width:100%!important;\n height:100%!important;\n }\n.leaflet-container.leaflet-fullscreen-on {\n width:100%!important;\n height:100%!important;\n }\n\n.leaflet-pseudo-fullscreen {\n position:fixed!important;\n width:100%!important;\n height:100%!important;\n top:0!important;\n left:0!important;\n z-index:99999;\n }\n\n@media\n (-webkit-min-device-pixel-ratio:2),\n (min-resolution:192dpi) {\n .leaflet-control-fullscreen a {\n background-image:url(fullscreen@2x.png);\n }\n }\n",".marker-icon {\n background-color: #ffffff;\n border: 1px solid #3388ff;\n border-radius: 50%;\n margin: -8px 0 0 -8px !important;\n width: 14px !important;\n height: 14px !important;\n outline: 0;\n transition: opacity ease 0.3s;\n}\n\n.marker-icon-middle {\n opacity: 0.7;\n margin: -6px 0 0 -6px !important;\n width: 10px !important;\n height: 10px !important;\n}\n\n.leaflet-pm-draggable {\n cursor: move !important;\n}\n\n.cursor-marker {\n cursor: crosshair;\n pointer-events: none;\n opacity: 0;\n}\n\n.cursor-marker.visible {\n opacity: 1 !important;\n}\n\n.geoman-draw-cursor {\n cursor: crosshair;\n}\n\n.rect-style-marker,\n.rect-start-marker {\n opacity: 0;\n}\n\n.rect-style-marker.visible,\n.rect-start-marker.visible {\n opacity: 1 !important;\n}\n\n.vertexmarker-disabled {\n opacity: 0.7;\n}\n\n.pm-text-marker {\n width: 0;\n height: 0;\n}\n\n.pm-textarea {\n background-color: #fff;\n color: #000;\n resize: none;\n border: none;\n outline: 0;\n cursor: pointer;\n border-radius: 3px;\n padding-left: 7px;\n padding-bottom: 0;\n padding-top: 4px;\n}\n\n.leaflet-pm-draggable .pm-textarea {\n cursor: move;\n}\n\n.pm-textarea:focus,\n.pm-textarea:focus-within,\n.pm-textarea:focus-visible,\n.pm-textarea:active {\n border: 2px solid #000;\n outline: 0;\n}\n\n.pm-textarea.pm-disabled {\n border: none;\n user-select: none;\n}\n\n.pm-textarea.pm-hasfocus {\n cursor: auto;\n}\n",".leaflet-pm-toolbar {\n}\n\n.leaflet-pm-toolbar .leaflet-buttons-control-button {\n padding: 5px;\n box-sizing: border-box;\n position: relative;\n z-index: 3;\n}\n\n.leaflet-pm-toolbar\n .leaflet-pm-actions-container\n a.leaflet-pm-action:first-child:not(.pos-right),\n.leaflet-pm-toolbar\n .leaflet-pm-actions-container\n a.leaflet-pm-action:last-child.pos-right {\n border-radius: 0;\n}\n\n.leaflet-pm-toolbar .button-container a.leaflet-buttons-control-button {\n border-radius: 0;\n}\n\n.leaflet-pm-toolbar\n .button-container:last-child\n a.leaflet-buttons-control-button {\n border-radius: 0 0 2px 2px;\n}\n\n.leaflet-pm-toolbar\n .button-container:first-child\n a.leaflet-buttons-control-button {\n border-radius: 2px 2px 0 0;\n}\n\n.leaflet-pm-toolbar\n .button-container:last-child\n a.leaflet-buttons-control-button {\n border-bottom: none;\n}\n\n.leaflet-pm-toolbar .control-fa-icon {\n font-size: 19px;\n line-height: 24px;\n}\n\n.leaflet-pm-toolbar .control-icon {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n background-size: contain;\n background-repeat: no-repeat;\n background-position: center center;\n}\n\n.leaflet-pm-toolbar .leaflet-pm-icon-marker {\n background-image: url('../assets/icons/Marker.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-polygon {\n background-image: url('../assets/icons/Polygon.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-polyline {\n background-image: url('../assets/icons/Line.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-circle {\n background-image: url('../assets/icons/Circle.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-circle-marker {\n background-image: url('../assets/icons/CircleMarker.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-rectangle {\n background-image: url('../assets/icons/Rectangle.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-delete {\n background-image: url('../assets/icons/Eraser.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-edit {\n background-image: url('../assets/icons/Edit_Vertex.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-drag {\n background-image: url('../assets/icons/Move.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-cut {\n background-image: url('../assets/icons/Scissors.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-snapping {\n background-image: url('../assets/icons/Magnet.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-rotate {\n background-image: url('../assets/icons/Rotate.svg');\n}\n.leaflet-pm-toolbar .leaflet-pm-icon-text {\n background-image: url('../assets/icons/Text.svg');\n}\n\n.leaflet-buttons-control-button:hover,\n.leaflet-buttons-control-button:focus {\n cursor: pointer;\n background-color: #f4f4f4;\n}\n.active > .leaflet-buttons-control-button {\n box-shadow: inset 0 -1px 5px 2px rgba(81, 77, 77, 0.31);\n}\n\n.leaflet-buttons-control-text-hide {\n display: none;\n}\n\n.button-container {\n position: relative;\n}\n\n.button-container .leaflet-pm-actions-container {\n z-index: 2;\n position: absolute;\n top: 0;\n left: 100%;\n display: none;\n white-space: nowrap;\n direction: ltr;\n}\n\n.leaflet-right\n .leaflet-pm-toolbar\n .button-container\n .leaflet-pm-actions-container {\n right: 100%;\n left: auto;\n}\n\n.button-container.active .leaflet-pm-actions-container {\n display: block;\n}\n\n.button-container\n .leaflet-pm-actions-container:not(.pos-right)\n a.leaflet-pm-action:last-child {\n border-radius: 0 3px 3px 0;\n border-right: 0;\n}\n.button-container\n .leaflet-pm-actions-container.pos-right\n a.leaflet-pm-action:first-child {\n border-radius: 3px 0 0 3px;\n}\n.button-container\n .leaflet-pm-actions-container.pos-right\n a.leaflet-pm-action:last-child {\n border-right: 0;\n}\n.button-container .leaflet-pm-actions-container .leaflet-pm-action {\n padding: 0 10px;\n background-color: #666;\n color: #fff;\n display: inline-block;\n width: auto;\n border-right: 1px solid #eee;\n user-select: none;\n border-bottom: none;\n height: 29px;\n line-height: 29px;\n vertical-align: middle;\n}\n.leaflet-pm-toolbar\n .button-container:first-child.pos-right.active\n a.leaflet-buttons-control-button {\n border-top-left-radius: 0;\n}\n.leaflet-pm-toolbar\n .button-container:first-child.active:not(.pos-right)\n a.leaflet-buttons-control-button {\n border-top-right-radius: 0;\n}\n\n.button-container .leaflet-pm-actions-container .leaflet-pm-action:hover,\n.button-container .leaflet-pm-actions-container .leaflet-pm-action:focus {\n cursor: pointer;\n background-color: #777;\n}\n\n/* That the active control is always over the other controls */\n.leaflet-pm-toolbar.activeChild {\n z-index: 801;\n}\n\n.leaflet-buttons-control-button.pm-disabled {\n background-color: #f4f4f4;\n}\n\n.leaflet-buttons-control-button.pm-disabled > .control-icon {\n filter: opacity(0.6);\n}\n","@import \"../../node_modules/leaflet/dist/leaflet.css\";\n@import \"../../node_modules/leaflet-fullscreen/dist/leaflet.fullscreen.css\";\n@import \"../../node_modules/@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css\";\n\n.map-icon {\n stroke: #dbeafe;\n}\n\n.map-location-button {\n position: absolute;\n bottom: 20px;\n right: 20px;\n width: 40px;\n height: 40px;\n background-color: #eee !important;\n border-radius: 50%;\n border: none;\n box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);\n cursor: pointer;\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 23332;\n color: #c83a3a;\n}\n\n.map-location-button svg {\n width: 24px;\n height: 24px;\n fill: #333;\n}"]} -------------------------------------------------------------------------------- /resources/js/index.js: -------------------------------------------------------------------------------- 1 | import * as LF from 'leaflet'; 2 | import 'leaflet-fullscreen'; 3 | import "@geoman-io/leaflet-geoman-free"; 4 | 5 | 6 | document.addEventListener('DOMContentLoaded', () => { 7 | const mapPicker = ($wire, config, state) => { 8 | return { 9 | map: null, 10 | tile: null, 11 | marker: null, 12 | rangeCircle: null, 13 | drawItems: null, 14 | rangeSelectField: null, 15 | formRestorationHiddenInput:null, 16 | debouncedUpdate: null, 17 | 18 | debounce: function(func, wait) { 19 | let timeout; 20 | return function executedFunction(...args) { 21 | const later = () => { 22 | clearTimeout(timeout); 23 | func(...args); 24 | }; 25 | clearTimeout(timeout); 26 | timeout = setTimeout(later, wait); 27 | }; 28 | }, 29 | 30 | createMap: function (el) { 31 | const that = this; 32 | 33 | this.map = LF.map(el, config.controls); 34 | 35 | if(config.bounds) 36 | { 37 | let southWest = LF.latLng(config.bounds.sw.lat, config.bounds.sw.lng); 38 | let northEast = LF.latLng(config.bounds.ne.lat, config.bounds.ne.lng); 39 | let bounds = LF.latLngBounds(southWest, northEast); 40 | this.map.setMaxBounds(bounds); 41 | this.map.fitBounds(bounds); 42 | this.map.on('drag', function() { 43 | map.panInsideBounds(bounds, { animate: false }); 44 | }); 45 | } 46 | this.map.on('load', () => { 47 | setTimeout(() => this.map.invalidateSize(true), 0); 48 | 49 | if (config.showMarker && !config.clickable) { 50 | this.marker.setLatLng(this.map.getCenter()); 51 | } 52 | }); 53 | 54 | if (!config.draggable) { 55 | this.map.dragging.disable(); 56 | } 57 | 58 | if(config.clickable) 59 | { 60 | this.map.on('click', function(e) { 61 | that.setCoordinates(e.latlng); 62 | }); 63 | } 64 | 65 | this.tile = LF.tileLayer(config.tilesUrl, { 66 | attribution: config.attribution, 67 | minZoom: config.minZoom, 68 | maxZoom: config.maxZoom, 69 | tileSize: config.tileSize, 70 | zoomOffset: config.zoomOffset, 71 | detectRetina: config.detectRetina, 72 | }).addTo(this.map); 73 | 74 | if (config.showMarker) { 75 | this.marker = LF.marker(this.getCoordinates(), { 76 | icon: this.createMarkerIcon(), 77 | draggable: false, 78 | autoPan: true 79 | }).addTo(this.map); 80 | this.setMarkerRange(); 81 | if(!config.clickable) { 82 | this.map.on('move', () => this.setCoordinates(this.map.getCenter())); 83 | } 84 | } 85 | 86 | if(!config.clickable) 87 | { 88 | this.map.on('moveend', () => this.updateLocation()); 89 | } 90 | 91 | this.map.on('locationfound', function () { 92 | that.map.setZoom(config.controls.zoom); 93 | }); 94 | 95 | let location = this.getCoordinates(); 96 | if (!location.lat && !location.lng) { 97 | this.map.locate({ 98 | setView: true, 99 | maxZoom: config.controls.maxZoom, 100 | enableHighAccuracy: true, 101 | watch: false 102 | }); 103 | } else { 104 | this.map.setView(new LF.LatLng(location.lat, location.lng)); 105 | } 106 | 107 | if (config.showMyLocationButton) { 108 | this.addLocationButton(); 109 | } 110 | 111 | if (config.liveLocation.send && config.liveLocation.realtime) { 112 | setInterval(() => { 113 | this.fetchCurrentLocation(); 114 | }, config.liveLocation.miliseconds); 115 | } 116 | this.map.on('zoomend',function(event) { 117 | that.setFormRestorationState(false, that.map.getZoom()); 118 | }); 119 | 120 | // Geoman setup 121 | if (config.geoMan.show) { 122 | this.map.pm.addControls({ 123 | snappable: config.geoMan.snappable, 124 | snapDistance: config.geoMan.snapDistance, 125 | position: config.geoMan.position, 126 | drawCircleMarker: config.geoMan.drawCircleMarker, 127 | rotateMode: config.geoMan.rotateMode, 128 | drawRectangle: config.geoMan.drawRectangle, 129 | drawText: config.geoMan.drawText, 130 | drawMarker: config.geoMan.drawMarker, 131 | drawPolygon: config.geoMan.drawPolygon, 132 | drawPolyline: config.geoMan.drawPolyline, 133 | drawCircle: config.geoMan.drawCircle, 134 | editMode: config.geoMan.editMode, 135 | dragMode: config.geoMan.dragMode, 136 | cutPolygon: config.geoMan.cutPolygon, 137 | editPolygon: config.geoMan.editPolygon, 138 | deleteLayer: config.geoMan.deleteLayer 139 | }); 140 | 141 | this.drawItems = new LF.FeatureGroup().addTo(this.map); 142 | 143 | this.map.on('pm:create', (e) => { 144 | if (e.layer && e.layer.pm) { 145 | e.layer.pm.enable(); 146 | 147 | if (e.shape === 'Circle') { 148 | const center = e.layer.getLatLng(); 149 | const radius = e.layer.getRadius(); 150 | 151 | e.layer.circleData = { 152 | center: center, 153 | radius: radius 154 | }; 155 | } 156 | 157 | this.drawItems.addLayer(e.layer); 158 | this.updateGeoJson(); 159 | } 160 | }); 161 | 162 | this.map.on('pm:edit', (e) => { 163 | if (e.layer && e.layer.getRadius) { 164 | e.layer.circleData = { 165 | center: e.layer.getLatLng(), 166 | radius: e.layer.getRadius() 167 | }; 168 | } 169 | this.updateGeoJson(); 170 | }); 171 | 172 | this.map.on('pm:remove', (e) => { 173 | try { 174 | this.drawItems.removeLayer(e.layer); 175 | this.updateGeoJson(); 176 | } catch (error) { 177 | console.error("Error during removal of layer:", error); 178 | } 179 | }); 180 | 181 | // Load existing GeoJSON if available 182 | const existingGeoJson = this.getGeoJson(); 183 | if (existingGeoJson) { 184 | this.drawItems = LF.geoJSON(existingGeoJson, { 185 | pointToLayer: (feature, latlng) => { 186 | if (feature.properties && feature.properties.type === "Circle") { 187 | 188 | const circle = LF.circle(latlng, { 189 | radius: feature.properties.radius, 190 | color: config.geoMan.color || "#3388ff", 191 | fillColor: config.geoMan.filledColor || '#cad9ec', 192 | fillOpacity: 0.4 193 | }); 194 | 195 | circle.circleData = { 196 | center: latlng, 197 | radius: feature.properties.radius 198 | }; 199 | 200 | return circle; 201 | } 202 | 203 | return LF.circleMarker(latlng, { 204 | radius: 15, 205 | color: '#3388ff', 206 | fillColor: '#3388ff', 207 | fillOpacity: 0.6 208 | }); 209 | }, 210 | style: function(feature) { 211 | if (feature.geometry.type === 'Polygon') { 212 | return { 213 | color: config.geoMan.color || "#3388ff", 214 | fillColor: config.geoMan.filledColor || 'blue', 215 | weight: 2, 216 | fillOpacity: 0.4 217 | }; 218 | } 219 | }, 220 | onEachFeature: (feature, layer) => { 221 | 222 | if (typeof feature.properties.title != "undefined") { 223 | layer.bindPopup(feature.properties.title); 224 | }else if (feature.geometry.type === 'Polygon') { 225 | layer.bindPopup("Polygon Area"); 226 | } else if (feature.geometry.type === 'Point') { 227 | layer.bindPopup("Point Location"); 228 | } 229 | 230 | 231 | if (config.geoMan.editable) { 232 | if (feature.geometry.type === 'Polygon') { 233 | layer.pm.enable({ 234 | allowSelfIntersection: false 235 | }); 236 | } else if (feature.geometry.type === 'Point') { 237 | layer.pm.enable({ 238 | draggable: true 239 | }); 240 | } 241 | } 242 | 243 | layer.on('pm:edit', () => { 244 | this.updateGeoJson(); 245 | }); 246 | } 247 | }).addTo(this.map); 248 | 249 | if(config.geoMan.editable){ 250 | 251 | this.drawItems.eachLayer(layer => { 252 | layer.pm.enable({ 253 | allowSelfIntersection: false, 254 | }); 255 | }); 256 | } 257 | 258 | this.map.fitBounds(this.drawItems.getBounds()); 259 | } 260 | } 261 | }, 262 | createMarkerIcon() { 263 | if (config.markerIconUrl) { 264 | return LF.icon({ 265 | iconUrl: config.markerIconUrl, 266 | iconSize: config.markerIconSize, 267 | iconAnchor: config.markerIconAnchor, 268 | className: config.markerIconClassName 269 | }); 270 | } 271 | 272 | const markerColor = config.markerColor || "#3b82f6"; 273 | const defaultHtml = ``; 274 | 275 | return LF.divIcon({ 276 | html: config.markerHtml || defaultHtml, 277 | className: config.markerIconClassName, 278 | iconSize: config.markerIconSize, 279 | iconAnchor: config.markerIconAnchor 280 | }); 281 | }, 282 | initFormRestoration: function () { 283 | this.formRestorationHiddenInput = document.getElementById(config.statePath+'_fmrest'); 284 | window.addEventListener("pageshow", (event) => { 285 | 286 | let restoredState = this.getFormRestorationState(); 287 | if(restoredState){ 288 | let coords = new LF.LatLng(restoredState.lat, restoredState.lng); 289 | config.zoom = restoredState.zoom; 290 | config.controls.zoom=restoredState.zoom; 291 | this.setCoordinates(coords); 292 | } 293 | }); 294 | 295 | }, 296 | setFormRestorationState: function(coords = null, zoom = null) { 297 | 298 | coords = coords || this.getFormRestorationState() || this.getCoordinates(); 299 | 300 | if (this.map) { 301 | coords.zoom = zoom ?? this.map.getZoom(); 302 | } 303 | 304 | this.formRestorationHiddenInput.value = JSON.stringify(coords); 305 | }, 306 | getFormRestorationState: function () { 307 | if(this.formRestorationHiddenInput.value) 308 | return JSON.parse(this.formRestorationHiddenInput.value); 309 | return false; 310 | }, 311 | updateGeoJson: function() { 312 | try { 313 | const geoJsonData = { 314 | type: "FeatureCollection", 315 | features: [] 316 | }; 317 | 318 | this.drawItems.eachLayer((layer) => { 319 | if (layer.getRadius) { // It's a circle 320 | const circleData = layer.circleData || { 321 | center: layer.getLatLng(), 322 | radius: layer.getRadius() 323 | }; 324 | 325 | geoJsonData.features.push({ 326 | type: "Feature", 327 | properties: { 328 | type: "Circle", 329 | radius: circleData.radius 330 | }, 331 | geometry: { 332 | type: "Point", 333 | coordinates: [circleData.center.lng, circleData.center.lat] 334 | } 335 | }); 336 | } else { 337 | 338 | const layerGeoJson = layer.toGeoJSON(); 339 | geoJsonData.features.push(layerGeoJson); 340 | } 341 | }); 342 | 343 | $wire.set(config.statePath, { 344 | ...$wire.get(config.statePath), 345 | lat: this.marker ? this.marker.getLatLng().lat : this.map.getCenter().lat, 346 | lng: this.marker ? this.marker.getLatLng().lng : this.map.getCenter().lng, 347 | geojson: geoJsonData 348 | }, true); 349 | 350 | } catch (error) { 351 | console.error("Error updating GeoJSON:", error); 352 | } 353 | }, 354 | 355 | getGeoJson: function() { 356 | const state = $wire.get(config.statePath) ?? {}; 357 | return state.geojson; 358 | }, 359 | updateLocation: function() { 360 | let oldCoordinates = this.getCoordinates(); 361 | let currentCoordinates = this.map.getCenter(); 362 | if(config.clickable) { 363 | currentCoordinates = this.marker.getLatLng(); 364 | } 365 | 366 | const minChange = config.minChange || 0.00001; 367 | if (Math.abs(oldCoordinates.lng - currentCoordinates.lng) > minChange || 368 | Math.abs(oldCoordinates.lat - currentCoordinates.lat) > minChange) { 369 | this.setCoordinates(currentCoordinates); 370 | this.setMarkerRange(); 371 | } 372 | }, 373 | 374 | removeMap: function (el) { 375 | if (this.marker) { 376 | this.marker.remove(); 377 | this.marker = null; 378 | } 379 | this.tile.remove(); 380 | this.tile = null; 381 | this.map.off(); 382 | this.map.remove(); 383 | this.map = null; 384 | }, 385 | 386 | getCoordinates: function () { 387 | if(state){ 388 | return state; 389 | } 390 | 391 | let location = $wire.get(config.statePath) ?? {}; 392 | 393 | const hasValidCoordinates = location.hasOwnProperty('lat') && location.hasOwnProperty('lng') && 394 | location.lat !== null && location.lng !== null; 395 | 396 | if (!hasValidCoordinates) { 397 | location = { 398 | lat: config.default.lat, 399 | lng: config.default.lng 400 | }; 401 | } 402 | 403 | return location; 404 | }, 405 | 406 | setCoordinates: function (coords) { 407 | 408 | if (this.marker && config.showMarker) { 409 | this.marker.setLatLng(coords); 410 | } 411 | this.setFormRestorationState(coords); 412 | 413 | if (!this.debouncedUpdate) { 414 | this.debouncedUpdate = this.debounce((coords) => { 415 | if(config.type === 'field'){ 416 | $wire.set(config.statePath, { 417 | ...$wire.get(config.statePath), 418 | lat: coords.lat, 419 | lng: coords.lng 420 | }); 421 | } 422 | 423 | if (config.liveLocation.send) { 424 | $wire.$refresh(); 425 | } 426 | this.updateMarker(); 427 | }, config.updateDelay || 500); 428 | } 429 | 430 | this.debouncedUpdate(coords); 431 | 432 | return coords; 433 | }, 434 | 435 | attach: function (el) { 436 | this.createMap(el); 437 | const observer = new IntersectionObserver(entries => { 438 | entries.forEach(entry => { 439 | if (entry.intersectionRatio > 0) { 440 | if (!this.map) 441 | this.createMap(el); 442 | } else { 443 | this.removeMap(el); 444 | } 445 | }); 446 | }, { 447 | root: null, 448 | rootMargin: '0px', 449 | threshold: 1.0 450 | }); 451 | observer.observe(el); 452 | }, 453 | 454 | fetchCurrentLocation: function () { 455 | if ('geolocation' in navigator) { 456 | navigator.geolocation.getCurrentPosition(async position => { 457 | const currentPosition = new LF.LatLng(position.coords.latitude, position.coords.longitude); 458 | await this.map.flyTo(currentPosition); 459 | 460 | this.updateLocation(); 461 | this.updateMarker(); 462 | }, error => { 463 | console.error('Error fetching current location:', error); 464 | }); 465 | } else { 466 | alert('Geolocation is not supported by this browser.'); 467 | } 468 | }, 469 | 470 | addLocationButton: function() { 471 | const locationButton = document.createElement('button'); 472 | locationButton.innerHTML = ''; 473 | locationButton.type = 'button'; 474 | locationButton.classList.add('map-location-button'); 475 | locationButton.onclick = () => this.fetchCurrentLocation(); 476 | this.map.getContainer().appendChild(locationButton); 477 | }, 478 | 479 | setMarkerRange: function() { 480 | 481 | if ((config.clickable && !this.marker) || !this.rangeSelectField) { 482 | return; 483 | } 484 | 485 | const distance = parseInt(this.rangeSelectField.value || 0); 486 | const coordinates = this.getCoordinates(); 487 | const circleStyle = { 488 | color: 'blue', 489 | fillColor: '#f03', 490 | fillOpacity: 0.5, 491 | radius: distance 492 | }; 493 | 494 | if (this.rangeCircle) { 495 | this.rangeCircle 496 | .setLatLng(coordinates) 497 | .setRadius(distance); 498 | return; 499 | } 500 | 501 | this.rangeCircle = LF.circle(coordinates, circleStyle).addTo(this.map); 502 | }, 503 | 504 | init: function() { 505 | this.$wire = $wire; 506 | this.config = config; 507 | this.state = state; 508 | this.rangeSelectField = document.getElementById(config.rangeSelectField); 509 | this.initFormRestoration(); 510 | 511 | let that=this 512 | if(this.rangeSelectField){ 513 | this.rangeSelectField.addEventListener('change', function () {that.updateMarker(); }); 514 | } 515 | $wire.on('refreshMap', this.refreshMap.bind(this)); 516 | }, 517 | 518 | updateMarker: function() { 519 | if (config.showMarker && this.marker) { 520 | this.marker.setLatLng(this.getCoordinates()); 521 | this.setMarkerRange(); 522 | this.updateLocation(); 523 | } 524 | }, 525 | 526 | refreshMap: function() { 527 | this.map.flyTo(this.getCoordinates()); 528 | this.updateMarker(); 529 | } 530 | }; 531 | }; 532 | 533 | window.mapPicker = mapPicker; 534 | 535 | window.dispatchEvent(new CustomEvent('map-script-loaded')); 536 | }); 537 | -------------------------------------------------------------------------------- /resources/lang/en/map-picker.php: -------------------------------------------------------------------------------- 1 | 10 |
17 |
20 |
21 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /resources/views/infolists/osm-map-entry.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | -------------------------------------------------------------------------------- /src/Contracts/MapOptions.php: -------------------------------------------------------------------------------- 1 | 'field', 27 | 'statePath' => '', 28 | 'draggable' => true, 29 | 'showMarker' => true, 30 | 'tilesUrl' => 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', 31 | 'attribution' => null, 32 | 'zoomOffset' => -1, 33 | 'tileSize' => 512, 34 | 'detectRetina' => true, 35 | 'rangeSelectField' => 'distance', 36 | 'minZoom' => 0, 37 | 'maxZoom' => 28, 38 | 'zoom' => 15, 39 | 'clickable' => false, 40 | 'markerColor' => '#3b82f6', 41 | 'liveLocation' => false, 42 | 'bounds' => false, 43 | 'showMyLocationButton' => [false, false, 5000], 44 | 'default' => ['lat' => 0, 'lng' => 0], 45 | 'markerHtml' => '', 46 | 'markerIconUrl' => null, 47 | 'markerIconSize' => [36, 36], 48 | 'markerIconClassName' => '', 49 | 'markerIconAnchor' => [18, 36], 50 | 'geoMan' => [ 51 | 'show' => false, 52 | 'editable' => true, 53 | 'position' => 'topleft', 54 | 'drawCircleMarker' => true, 55 | 'rotateMode' => true, 56 | 'drawMarker' => true, 57 | 'drawPolygon' => true, 58 | 'drawPolyline' => true, 59 | 'drawCircle' => true, 60 | 'dragMode' => true, 61 | 'cutPolygon' => true, 62 | 'editPolygon' => true, 63 | 'deleteLayer' => true, 64 | 'color' => '#3388ff', 65 | 'filledColor' => '#cad9ec', 66 | 'snappable' => false, 67 | 'snapDistance' => 20, 68 | 'drawText' => true, 69 | 'drawRectangle' => true 70 | ] 71 | ]; 72 | 73 | /** 74 | * Leaflet controls variables 75 | * @var array 76 | */ 77 | private array $controls = [ 78 | 'zoomControl' => true, 79 | 'scrollWheelZoom' => 'center', 80 | 'doubleClickZoom' => 'center', 81 | 'touchZoom' => 'center', 82 | 'minZoom' => 1, 83 | 'maxZoom' => 28, 84 | 'zoom' => 15, 85 | 'fullscreenControl' => true, 86 | ]; 87 | 88 | private array $extraStyle = []; 89 | 90 | /** 91 | * Extra leaflet controls variables 92 | * @var array 93 | */ 94 | private array $extraControls = []; 95 | 96 | /** 97 | * Create json configuration string 98 | * @return string 99 | */ 100 | public function getMapConfig(): string 101 | { 102 | $statePath = $this->getStatePath(); 103 | $lastDotPosition = mb_strrpos($statePath, '.'); 104 | $rangeSelectField = mb_substr($statePath, 0, $lastDotPosition + 1).$this->mapConfig['rangeSelectField']; 105 | return json_encode( 106 | array_merge($this->mapConfig, [ 107 | 'statePath' => $statePath, 108 | 'rangeSelectField' => $rangeSelectField, 109 | 'controls' => array_merge($this->controls, $this->extraControls) 110 | ]) 111 | ); 112 | } 113 | 114 | 115 | /** 116 | * Create extra styles string 117 | * @return string 118 | */ 119 | public function getExtraStyle(): string 120 | { 121 | return implode(';', $this->extraStyle); 122 | } 123 | 124 | /** 125 | * Determines if the user can click to place the marker on the map. 126 | * @param Closure|bool $clickable 127 | * @return $this 128 | */ 129 | public function clickable(Closure|bool $clickable): self 130 | { 131 | $this->mapConfig['clickable'] = $this->evaluate($clickable); 132 | return $this; 133 | } 134 | 135 | /** 136 | * Determine if user can drag map around or not. 137 | * @param Closure|bool $draggable 138 | * @return MapOptions 139 | * @note Default value is false 140 | */ 141 | public function draggable(Closure|bool $draggable = true): self 142 | { 143 | $this->mapConfig['draggable'] = $this->evaluate($draggable); 144 | 145 | return $this; 146 | } 147 | 148 | 149 | /** 150 | * Prevents the map from panning outside the defined box, and sets 151 | * a default location in the center of the box. It makes sense to 152 | * use this with a minimum zoom that suits the size of your map and 153 | * the size of the box or the way it pans back to the bounding box 154 | * looks strange. You can call with $on set to false to undo this. 155 | * 156 | * @param Closure|bool $on 157 | * @param int|float $southWestLat 158 | * @param int|float $southWestLng 159 | * @param int|float $northEastLat 160 | * @param int|float $northEastLng 161 | * @return self 162 | */ 163 | public function boundaries(Closure|bool $on, int|float $southWestLat = 0, int|float $southWestLng = 0, int|float $northEastLat = 0, int|float $northEastLng = 0): self 164 | { 165 | if ( ! $this->evaluate($on)) { 166 | $this->mapConfig['boundaries'] = false; 167 | 168 | return $this; 169 | } 170 | 171 | $this->mapConfig['bounds']['sw'] = ['lat' => $southWestLat, 'lng' => $southWestLng]; 172 | $this->mapConfig['bounds']['ne'] = ['lat' => $northEastLat, 'lng' => $northEastLng]; 173 | $this->defaultLocation(($southWestLat + $northEastLat) / 2.0, ($southWestLng + $northEastLng) / 2.0); 174 | 175 | return $this; 176 | } 177 | 178 | /** 179 | * Convenience function for appropriate values for boundaries() when 180 | * you want the British Isles 181 | * @return self 182 | **/ 183 | public function setBoundsToBritishIsles(): self 184 | { 185 | $this->boundaries(true, 49.5, -11, 61, 2); 186 | return $this; 187 | } 188 | 189 | 190 | public function defaultLocation(int|float $latitude, float|int $longitude): self 191 | { 192 | $this->mapConfig['default']['lat'] = $latitude; 193 | $this->mapConfig['default']['lng'] = $longitude; 194 | 195 | return $this; 196 | } 197 | 198 | 199 | /** 200 | * Set extra style 201 | * @param array $styles 202 | * @return self 203 | */ 204 | public function extraStyles(array $styles = []): self 205 | { 206 | $this->extraStyle = $styles; 207 | return $this; 208 | } 209 | 210 | 211 | /** 212 | * Set default zoom 213 | * @param int $zoom 214 | * @return MapOptions 215 | * @note Default value 19 216 | */ 217 | public function zoom(int $zoom): self 218 | { 219 | $this->controls['zoom'] = $zoom; 220 | return $this; 221 | } 222 | 223 | /** 224 | * Set max zoom 225 | * @param int $maxZoom 226 | * @return $this 227 | * @note Default value 20 228 | */ 229 | public function maxZoom(int $maxZoom): self 230 | { 231 | $this->controls['maxZoom'] = $maxZoom; 232 | return $this; 233 | } 234 | 235 | /** 236 | * Set min zoom 237 | * @param int $maxZoom 238 | * @return $this 239 | * @note Default value 1 240 | */ 241 | public function minZoom(int $minZoom): self 242 | { 243 | $this->controls['minZoom'] = $minZoom; 244 | return $this; 245 | } 246 | 247 | /** 248 | * Determine if marker is visible or not. 249 | * @param Closure|bool $show 250 | * @return $this 251 | * @note Default value is false 252 | */ 253 | public function showMarker(Closure|bool $show = true): self 254 | { 255 | $this->mapConfig['showMarker'] = $this->evaluate($show); 256 | return $this; 257 | } 258 | 259 | /** 260 | * Set tiles url 261 | * @param string $url 262 | * @return $this 263 | */ 264 | public function tilesUrl(string $url): self 265 | { 266 | $this->mapConfig['tilesUrl'] = $url; 267 | return $this; 268 | } 269 | 270 | 271 | /** 272 | * Use the value of another field on the form for the range of the 273 | * circle surrounding the marker 274 | * @param string $rangeSelectField, 275 | * return $this 276 | **/ 277 | public function rangeSelectField(string $rangeSelectField): self 278 | { 279 | $this->mapConfig['rangeSelectField'] = $rangeSelectField; 280 | return $this; 281 | } 282 | 283 | /** 284 | * Determine if it detects retina monitors or not. 285 | * @param Closure|bool $detectRetina 286 | * @return $this 287 | */ 288 | public function detectRetina(Closure|bool $detectRetina = true): self 289 | { 290 | $this->mapConfig['detectRetina'] = $this->evaluate($detectRetina); 291 | return $this; 292 | } 293 | 294 | /** 295 | * Determine if zoom box is visible or not. 296 | * @param Closure|bool $show 297 | * @return $this 298 | */ 299 | public function showZoomControl(Closure|bool $show = true): self 300 | { 301 | $this->controls['zoomControl'] = $this->evaluate($show); 302 | return $this; 303 | } 304 | 305 | 306 | /** 307 | * Determine if fullscreen box is visible or not. 308 | * @param Closure|bool $show 309 | * @return $this 310 | */ 311 | public function showFullscreenControl(Closure|bool $show = true): self 312 | { 313 | $this->controls['fullscreenControl'] = $this->evaluate($show); 314 | return $this; 315 | } 316 | 317 | /** 318 | * Change the marker color. 319 | * @param string $color 320 | * @return $this 321 | */ 322 | public function markerColor(string $color): self 323 | { 324 | $this->mapConfig['markerColor'] = $color; 325 | return $this; 326 | } 327 | 328 | /** 329 | * Enable or disable live location updates for the map. 330 | * @param Closure|bool $send 331 | * @return $this 332 | */ 333 | public function liveLocation(Closure|bool $send = true, Closure|bool $realtime = false, int $miliseconds = 5000): self 334 | { 335 | $this->mapConfig['liveLocation'] = [ 336 | 'send' => $this->evaluate($send), 337 | 'realtime' => $this->evaluate($realtime), 338 | 'miliseconds' => $miliseconds 339 | ]; 340 | return $this; 341 | } 342 | 343 | /** 344 | * Enable or disable show my location button on map. 345 | * @param Closure|bool $showMyLocationButton 346 | * @return $this 347 | */ 348 | public function showMyLocationButton(Closure|bool $showMyLocationButton = true): self 349 | { 350 | $this->mapConfig['showMyLocationButton'] = $this->evaluate($showMyLocationButton); 351 | return $this; 352 | } 353 | 354 | /** 355 | * Append extra controls to be passed to leaflet map object 356 | * @param array $control 357 | * @return $this 358 | */ 359 | public function extraControl(array $control): self 360 | { 361 | $this->extraControls = array_merge($this->extraControls, $control); 362 | return $this; 363 | } 364 | 365 | /** 366 | * Append extra controls to be passed to leaflet tileLayer object 367 | * @param array $control 368 | * @return $this 369 | */ 370 | public function extraTileControl(array $control): self 371 | { 372 | $this->mapConfig = array_merge($this->mapConfig, $control); 373 | return $this; 374 | } 375 | 376 | 377 | /** 378 | * Enable or disable GeoMan functionality. 379 | * @param Closure|bool $show 380 | * @return $this 381 | */ 382 | public function geoMan(Closure|bool $show = true): self 383 | { 384 | $this->mapConfig['geoMan']['show'] = $this->evaluate($show); 385 | return $this; 386 | } 387 | 388 | 389 | /** 390 | * Enable or disable GeoMan edit mode. 391 | * @param Closure|bool $show 392 | * @return $this 393 | */ 394 | public function geoManEditable(Closure|bool $show = true): self 395 | { 396 | $this->mapConfig['geoMan']['editable'] = $this->evaluate($show); 397 | return $this; 398 | } 399 | 400 | /** 401 | * Set GeoMan control position. 402 | * @param string $position 403 | * @return $this 404 | * @note Valid values: 'topleft', 'topright', 'bottomleft', 'bottomright' 405 | */ 406 | public function geoManPosition(string $position = 'topleft'): self 407 | { 408 | $this->mapConfig['geoMan']['position'] = $position; 409 | return $this; 410 | } 411 | 412 | /** 413 | * Enable or disable drawing of circle markers. 414 | * @param Closure|bool $draw 415 | * @return $this 416 | */ 417 | public function drawCircleMarker(Closure|bool $draw = true): self 418 | { 419 | $this->mapConfig['geoMan']['drawCircleMarker'] = $this->evaluate($draw); 420 | return $this; 421 | } 422 | 423 | /** 424 | * Enable or disable Snappable. 425 | * @param Closure|bool $draw 426 | * @return $this 427 | */ 428 | public function snappable(Closure|bool $snappable = true, int $distance = 20): self 429 | { 430 | $this->mapConfig['geoMan']['snappable'] = $this->evaluate($snappable); 431 | $this->mapConfig['geoMan']['snapDistance'] = $distance; 432 | return $this; 433 | } 434 | 435 | /** 436 | * Enable or disable drawing of rectangle. 437 | * @param Closure|bool $draw 438 | * @return $this 439 | */ 440 | public function drawRectangle(Closure|bool $draw = true): self 441 | { 442 | $this->mapConfig['geoMan']['drawRectangle'] = $this->evaluate($draw); 443 | return $this; 444 | } 445 | 446 | /** 447 | * Enable or disable drawing of text. 448 | * @param Closure|bool $draw 449 | * @return $this 450 | */ 451 | public function drawText(Closure|bool $draw = true): self 452 | { 453 | $this->mapConfig['geoMan']['drawText'] = $this->evaluate($draw); 454 | return $this; 455 | } 456 | 457 | /** 458 | * Enable or disable rotate mode. 459 | * @param Closure|bool $rotate 460 | * @return $this 461 | */ 462 | public function rotateMode(Closure|bool $rotate = true): self 463 | { 464 | $this->mapConfig['geoMan']['rotateMode'] = $this->evaluate($rotate); 465 | return $this; 466 | } 467 | 468 | /** 469 | * Enable or disable drawing of markers. 470 | * @param Closure|bool $draw 471 | * @return $this 472 | */ 473 | public function drawMarker(Closure|bool $draw = true): self 474 | { 475 | $this->mapConfig['geoMan']['drawMarker'] = $this->evaluate($draw); 476 | return $this; 477 | } 478 | 479 | /** 480 | * Enable or disable drawing of polygons. 481 | * @param Closure|bool $draw 482 | * @return $this 483 | */ 484 | public function drawPolygon(Closure|bool $draw = true): self 485 | { 486 | $this->mapConfig['geoMan']['drawPolygon'] = $this->evaluate($draw); 487 | return $this; 488 | } 489 | 490 | /** 491 | * Enable or disable drawing of polylines. 492 | * @param Closure|bool $draw 493 | * @return $this 494 | */ 495 | public function drawPolyline(Closure|bool $draw = true): self 496 | { 497 | $this->mapConfig['geoMan']['drawPolyline'] = $this->evaluate($draw); 498 | return $this; 499 | } 500 | 501 | /** 502 | * Enable or disable drawing of circles. 503 | * @param Closure|bool $draw 504 | * @return $this 505 | */ 506 | public function drawCircle(Closure|bool $draw = true): self 507 | { 508 | $this->mapConfig['geoMan']['drawCircle'] = $this->evaluate($draw); 509 | return $this; 510 | } 511 | 512 | /** 513 | * Enable or disable editing of polygons. 514 | * @param Closure|bool $edit 515 | * @return $this 516 | */ 517 | public function editPolygon(Closure|bool $edit = true): self 518 | { 519 | $this->mapConfig['geoMan']['editPolygon'] = $this->evaluate($edit); 520 | return $this; 521 | } 522 | 523 | /** 524 | * Enable or disable deletion of layers. 525 | * @param Closure|bool $delete 526 | * @return $this 527 | */ 528 | public function deleteLayer(Closure|bool $delete = true): self 529 | { 530 | $this->mapConfig['geoMan']['deleteLayer'] = $this->evaluate($delete); 531 | return $this; 532 | } 533 | 534 | 535 | /** 536 | * Enable or disable drag mode. 537 | * @param Closure|bool $enable 538 | * @return $this 539 | */ 540 | public function dragMode(Closure|bool $enable = true): self 541 | { 542 | $this->mapConfig['geoMan']['dragMode'] = $this->evaluate($enable); 543 | return $this; 544 | } 545 | 546 | /** 547 | * Enable or disable polygon cutting. 548 | * @param Closure|bool $enable 549 | * @return $this 550 | */ 551 | public function cutPolygon(Closure|bool $enable = true): self 552 | { 553 | $this->mapConfig['geoMan']['cutPolygon'] = $this->evaluate($enable); 554 | return $this; 555 | } 556 | 557 | /** 558 | * Set the stroke color for drawings. 559 | * @param string $color 560 | * @return $this 561 | */ 562 | public function setColor(string $color): self 563 | { 564 | $this->mapConfig['geoMan']['color'] = $color; 565 | return $this; 566 | } 567 | 568 | /** 569 | * Set the fill color for drawings. 570 | * @param string $filledColor 571 | * @return $this 572 | */ 573 | public function setFilledColor(string $filledColor): self 574 | { 575 | $this->mapConfig['geoMan']['filledColor'] = $filledColor; 576 | return $this; 577 | } 578 | 579 | /** 580 | * Set custom HTML for marker icon 581 | * @param string $html 582 | * @return $this 583 | */ 584 | public function markerHtml(string $html): self 585 | { 586 | $this->mapConfig['markerHtml'] = $html; 587 | return $this; 588 | } 589 | 590 | /** 591 | * Set marker icon URL 592 | * @param string|null $url 593 | * @return $this 594 | */ 595 | public function markerIconUrl(?string $url): self 596 | { 597 | $this->mapConfig['markerIconUrl'] = $url; 598 | return $this; 599 | } 600 | 601 | /** 602 | * Set marker icon size 603 | * @param array $size 604 | * @return $this 605 | */ 606 | public function markerIconSize(array $size): self 607 | { 608 | $this->mapConfig['markerIconSize'] = $size; 609 | return $this; 610 | } 611 | 612 | /** 613 | * Set marker icon class name 614 | * @param string $className 615 | * @return $this 616 | */ 617 | public function markerIconClassName(string $className): self 618 | { 619 | $this->mapConfig['markerIconClassName'] = $className; 620 | return $this; 621 | } 622 | 623 | /** 624 | * Set marker icon anchor point 625 | * @param array $anchor 626 | * @return $this 627 | */ 628 | public function markerIconAnchor(array $anchor): self 629 | { 630 | $this->mapConfig['markerIconAnchor'] = $anchor; 631 | return $this; 632 | } 633 | 634 | /** 635 | * Setup function 636 | * @return void 637 | */ 638 | protected function setUp(): void 639 | { 640 | parent::setUp(); 641 | } 642 | } 643 | -------------------------------------------------------------------------------- /src/Infolists/MapEntry.php: -------------------------------------------------------------------------------- 1 | 'entry', 25 | 'statePath' => '', 26 | 'draggable' => true, 27 | 'showMarker' => true, 28 | 'tilesUrl' => 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', 29 | 'attribution' => null, 30 | 'zoomOffset' => -1, 31 | 'tileSize' => 512, 32 | 'detectRetina' => true, 33 | 'rangeSelectField' => 'distance', 34 | 'minZoom' => 0, 35 | 'maxZoom' => 28, 36 | 'zoom' => 15, 37 | 'clickable' => false, 38 | 'markerColor' => '#3b82f6', 39 | 'liveLocation' => false, 40 | 'bounds' => false, 41 | 'showMyLocationButton' => [false, false, 5000], 42 | 'default' => ['lat' => 0, 'lng' => 0], 43 | 'markerHtml' => '', 44 | 'markerIconUrl' => null, 45 | 'markerIconSize' => [36, 36], 46 | 'markerIconClassName' => '', 47 | 'markerIconAnchor' => [18, 36], 48 | 'geoMan' => [ 49 | 'show' => false, 50 | 'editable' => true, 51 | 'position' => 'topleft', 52 | 'drawCircleMarker' => true, 53 | 'rotateMode' => true, 54 | 'drawMarker' => true, 55 | 'drawPolygon' => true, 56 | 'drawPolyline' => true, 57 | 'drawCircle' => true, 58 | 'dragMode' => true, 59 | 'cutPolygon' => true, 60 | 'editPolygon' => true, 61 | 'deleteLayer' => true, 62 | 'color' => '#3388ff', 63 | 'filledColor' => '#cad9ec', 64 | 'snappable' => false, 65 | 'snapDistance' => 20, 66 | 'drawText' => true, 67 | 'drawRectangle' => true 68 | ] 69 | ]; 70 | 71 | /** 72 | * Leaflet controls variables 73 | * @var array 74 | */ 75 | private array $controls = [ 76 | 'zoomControl' => true, 77 | 'scrollWheelZoom' => 'center', 78 | 'doubleClickZoom' => 'center', 79 | 'touchZoom' => 'center', 80 | 'minZoom' => 1, 81 | 'maxZoom' => 28, 82 | 'zoom' => 15, 83 | 'fullscreenControl' => true, 84 | ]; 85 | 86 | private array $extraStyle = []; 87 | 88 | /** 89 | * Extra leaflet controls variables 90 | * @var array 91 | */ 92 | private array $extraControls = []; 93 | 94 | 95 | /** 96 | * Create json configuration string 97 | * @return string 98 | */ 99 | public function getMapConfig(): string 100 | { 101 | return json_encode( 102 | array_merge($this->mapConfig, [ 103 | 'statePath' => $this->getStatePath(), 104 | 'controls' => array_merge($this->controls, $this->extraControls) 105 | ]) 106 | ); 107 | } 108 | 109 | 110 | /** 111 | * Create extra styles string 112 | * @return string 113 | */ 114 | public function getExtraStyle(): string 115 | { 116 | return implode(';', $this->extraStyle); 117 | } 118 | 119 | 120 | /** 121 | * Determine if user can drag map around or not. 122 | * @param Closure|bool $draggable 123 | * @return MapOptions 124 | * @note Default value is false 125 | */ 126 | public function draggable(Closure|bool $draggable = true): self 127 | { 128 | $this->mapConfig['draggable'] = $this->evaluate($draggable); 129 | return $this; 130 | } 131 | 132 | 133 | 134 | public function defaultLocation(int|float $latitude, float|int $longitude): self 135 | { 136 | $this->mapConfig['default']['lat'] = $latitude; 137 | $this->mapConfig['default']['lng'] = $longitude; 138 | 139 | return $this; 140 | } 141 | 142 | 143 | /** 144 | * Set extra style 145 | * @param array $styles 146 | * @return self 147 | */ 148 | public function extraStyles(array $styles = []): self 149 | { 150 | $this->extraStyle = $styles; 151 | return $this; 152 | } 153 | 154 | 155 | /** 156 | * Set default zoom 157 | * @param int $zoom 158 | * @return MapOptions 159 | * @note Default value 19 160 | */ 161 | public function zoom(int $zoom): self 162 | { 163 | $this->controls['zoom'] = $zoom; 164 | return $this; 165 | } 166 | 167 | /** 168 | * Set max zoom 169 | * @param int $maxZoom 170 | * @return $this 171 | * @note Default value 20 172 | */ 173 | public function maxZoom(int $maxZoom): self 174 | { 175 | $this->controls['maxZoom'] = $maxZoom; 176 | return $this; 177 | } 178 | 179 | /** 180 | * Set min zoom 181 | * @param int $maxZoom 182 | * @return $this 183 | * @note Default value 1 184 | */ 185 | public function minZoom(int $minZoom): self 186 | { 187 | $this->controls['minZoom'] = $minZoom; 188 | return $this; 189 | } 190 | 191 | 192 | /** 193 | * Determine if marker is visible or not. 194 | * @param Closure|bool $show 195 | * @return $this 196 | * @note Default value is false 197 | */ 198 | public function showMarker(Closure|bool $show = true): self 199 | { 200 | $this->mapConfig['showMarker'] = $this->evaluate($show); 201 | return $this; 202 | } 203 | 204 | /** 205 | * Set tiles url 206 | * @param string $url 207 | * @return $this 208 | */ 209 | public function tilesUrl(string $url): self 210 | { 211 | $this->mapConfig['tilesUrl'] = $url; 212 | return $this; 213 | } 214 | 215 | /** 216 | * Determine if it detects retina monitors or not. 217 | * @param Closure|bool $detectRetina 218 | * @return $this 219 | */ 220 | public function detectRetina(Closure|bool $detectRetina = true): self 221 | { 222 | $this->mapConfig['detectRetina'] = $this->evaluate($detectRetina); 223 | return $this; 224 | } 225 | 226 | /** 227 | * Determine if zoom box is visible or not. 228 | * @param Closure|bool $show 229 | * @return $this 230 | */ 231 | public function showZoomControl(Closure|bool $show = true): self 232 | { 233 | $this->controls['zoomControl'] = $this->evaluate($show); 234 | return $this; 235 | } 236 | 237 | 238 | /** 239 | * Determine if fullscreen box is visible or not. 240 | * @param Closure|bool $show 241 | * @return $this 242 | */ 243 | public function showFullscreenControl(Closure|bool $show = true): self 244 | { 245 | $this->controls['fullscreenControl'] = $this->evaluate($show); 246 | return $this; 247 | } 248 | 249 | /** 250 | * Change the marker color. 251 | * @param string $color 252 | * @return $this 253 | */ 254 | public function markerColor(string $color): self 255 | { 256 | $this->mapConfig['markerColor'] = $color; 257 | return $this; 258 | } 259 | 260 | /** 261 | * Enable or disable live location updates for the map. 262 | * @param Closure|bool $send 263 | * @return $this 264 | */ 265 | public function liveLocation(Closure|bool $send = true, Closure|bool $realtime = false, int $miliseconds = 5000): self 266 | { 267 | $this->mapConfig['liveLocation'] = [ 268 | 'send' => $this->evaluate($send), 269 | 'realtime' => $this->evaluate($realtime), 270 | 'miliseconds' => $miliseconds 271 | ]; 272 | return $this; 273 | } 274 | 275 | /** 276 | * Enable or disable show my location button on map. 277 | * @param Closure|bool $showMyLocationButton 278 | * @return $this 279 | */ 280 | public function showMyLocationButton(Closure|bool $showMyLocationButton = true): self 281 | { 282 | $this->mapConfig['showMyLocationButton'] = $this->evaluate($showMyLocationButton); 283 | return $this; 284 | } 285 | 286 | /** 287 | * Append extra controls to be passed to leaflet map object 288 | * @param array $control 289 | * @return $this 290 | */ 291 | public function extraControl(array $control): self 292 | { 293 | $this->extraControls = array_merge($this->extraControls, $control); 294 | return $this; 295 | } 296 | 297 | /** 298 | * Append extra controls to be passed to leaflet tileLayer object 299 | * @param array $control 300 | * @return $this 301 | */ 302 | public function extraTileControl(array $control): self 303 | { 304 | $this->mapConfig = array_merge($this->mapConfig, $control); 305 | return $this; 306 | } 307 | 308 | /** 309 | * Determines if the user can click to place the marker on the map. 310 | * @param Closure|bool $clickable 311 | * @return $this 312 | */ 313 | public function clickable(Closure|bool $clickable): self 314 | { 315 | $this->mapConfig['clickable'] = $this->evaluate($clickable); 316 | return $this; 317 | } 318 | 319 | /** 320 | * Prevents the map from panning outside the defined box, and sets 321 | * a default location in the center of the box. 322 | * @param Closure|boolean $on 323 | * @param int|float $southWestLat 324 | * @param int|float $southWestLng 325 | * @param int|float $northEastLat 326 | * @param int|float $northEastLng 327 | * @return self 328 | */ 329 | public function boundaries(Closure|bool $on, int|float $southWestLat = 0, int|float $southWestLng = 0, int|float $northEastLat = 0, int|float $northEastLng = 0): self 330 | { 331 | if ( ! $this->evaluate($on)) { 332 | $this->mapConfig['boundaries'] = false; 333 | return $this; 334 | } 335 | 336 | $this->mapConfig['bounds']['sw'] = ['lat' => $southWestLat, 'lng' => $southWestLng]; 337 | $this->mapConfig['bounds']['ne'] = ['lat' => $northEastLat, 'lng' => $northEastLng]; 338 | $this->defaultLocation(($southWestLat + $northEastLat) / 2.0, ($southWestLng + $northEastLng) / 2.0); 339 | 340 | return $this; 341 | } 342 | 343 | /** 344 | * Convenience function for appropriate values for boundaries() when 345 | * you want the British Isles 346 | * @return self 347 | **/ 348 | public function setBoundsToBritishIsles(): self 349 | { 350 | $this->boundaries(true, 49.5, -11, 61, 2); 351 | return $this; 352 | } 353 | 354 | /** 355 | * Use the value of another field on the form for the range of the 356 | * circle surrounding the marker 357 | * @param string $rangeSelectField 358 | * @return $this 359 | **/ 360 | public function rangeSelectField(string $rangeSelectField): self 361 | { 362 | $this->mapConfig['rangeSelectField'] = $rangeSelectField; 363 | return $this; 364 | } 365 | 366 | /** 367 | * Enable or disable GeoMan functionality. 368 | * @param Closure|bool $show 369 | * @return $this 370 | */ 371 | public function geoMan(Closure|bool $show = true): self 372 | { 373 | $this->mapConfig['geoMan']['show'] = $this->evaluate($show); 374 | return $this; 375 | } 376 | 377 | /** 378 | * Enable or disable GeoMan edit mode. 379 | * @param Closure|bool $show 380 | * @return $this 381 | */ 382 | public function geoManEditable(Closure|bool $show = true): self 383 | { 384 | $this->mapConfig['geoMan']['editable'] = $this->evaluate($show); 385 | return $this; 386 | } 387 | 388 | /** 389 | * Set GeoMan control position. 390 | * @param string $position 391 | * @return $this 392 | * @note Valid values: 'topleft', 'topright', 'bottomleft', 'bottomright' 393 | */ 394 | public function geoManPosition(string $position = 'topleft'): self 395 | { 396 | $this->mapConfig['geoMan']['position'] = $position; 397 | return $this; 398 | } 399 | 400 | /** 401 | * Enable or disable drawing of circle markers. 402 | * @param Closure|bool $draw 403 | * @return $this 404 | */ 405 | public function drawCircleMarker(Closure|bool $draw = true): self 406 | { 407 | $this->mapConfig['geoMan']['drawCircleMarker'] = $this->evaluate($draw); 408 | return $this; 409 | } 410 | 411 | /** 412 | * Enable or disable rotate mode. 413 | * @param Closure|bool $rotate 414 | * @return $this 415 | */ 416 | public function rotateMode(Closure|bool $rotate = true): self 417 | { 418 | $this->mapConfig['geoMan']['rotateMode'] = $this->evaluate($rotate); 419 | return $this; 420 | } 421 | 422 | /** 423 | * Enable or disable drawing of markers. 424 | * @param Closure|bool $draw 425 | * @return $this 426 | */ 427 | public function drawMarker(Closure|bool $draw = true): self 428 | { 429 | $this->mapConfig['geoMan']['drawMarker'] = $this->evaluate($draw); 430 | return $this; 431 | } 432 | 433 | /** 434 | * Enable or disable drawing of polygons. 435 | * @param Closure|bool $draw 436 | * @return $this 437 | */ 438 | public function drawPolygon(Closure|bool $draw = true): self 439 | { 440 | $this->mapConfig['geoMan']['drawPolygon'] = $this->evaluate($draw); 441 | return $this; 442 | } 443 | 444 | /** 445 | * Enable or disable drawing of polylines. 446 | * @param Closure|bool $draw 447 | * @return $this 448 | */ 449 | public function drawPolyline(Closure|bool $draw = true): self 450 | { 451 | $this->mapConfig['geoMan']['drawPolyline'] = $this->evaluate($draw); 452 | return $this; 453 | } 454 | 455 | /** 456 | * Enable or disable drawing of circles. 457 | * @param Closure|bool $draw 458 | * @return $this 459 | */ 460 | public function drawCircle(Closure|bool $draw = true): self 461 | { 462 | $this->mapConfig['geoMan']['drawCircle'] = $this->evaluate($draw); 463 | return $this; 464 | } 465 | 466 | /** 467 | * Enable or disable editing of polygons. 468 | * @param Closure|bool $edit 469 | * @return $this 470 | */ 471 | public function editPolygon(Closure|bool $edit = true): self 472 | { 473 | $this->mapConfig['geoMan']['editPolygon'] = $this->evaluate($edit); 474 | return $this; 475 | } 476 | 477 | /** 478 | * Enable or disable deletion of layers. 479 | * @param Closure|bool $delete 480 | * @return $this 481 | */ 482 | public function deleteLayer(Closure|bool $delete = true): self 483 | { 484 | $this->mapConfig['geoMan']['deleteLayer'] = $this->evaluate($delete); 485 | return $this; 486 | } 487 | 488 | /** 489 | * Enable or disable drag mode. 490 | * @param Closure|bool $enable 491 | * @return $this 492 | */ 493 | public function dragMode(Closure|bool $enable = true): self 494 | { 495 | $this->mapConfig['geoMan']['dragMode'] = $this->evaluate($enable); 496 | return $this; 497 | } 498 | 499 | /** 500 | * Enable or disable snappable. 501 | * @param Closure|bool $snappable 502 | * @param int $distance 503 | * @return $this 504 | */ 505 | public function snappable(Closure|bool $snappable = true, int $distance = 20): self 506 | { 507 | $this->extraControls['snappable'] = $this->evaluate($snappable); 508 | $this->extraControls['snapDistance'] = $distance; 509 | return $this; 510 | } 511 | 512 | /** 513 | * Enable or disable drawing of rectangles. 514 | * @param Closure|bool $draw 515 | * @return $this 516 | */ 517 | public function drawRectangle(Closure|bool $draw = true): self 518 | { 519 | $this->extraControls['drawRectangle'] = $this->evaluate($draw); 520 | return $this; 521 | } 522 | 523 | /** 524 | * Enable or disable drawing of text. 525 | * @param Closure|bool $draw 526 | * @return $this 527 | */ 528 | public function drawText(Closure|bool $draw = true): self 529 | { 530 | $this->extraControls['drawText'] = $this->evaluate($draw); 531 | return $this; 532 | } 533 | 534 | /** 535 | * Enable or disable polygon cutting. 536 | * @param Closure|bool $enable 537 | * @return $this 538 | */ 539 | public function cutPolygon(Closure|bool $enable = true): self 540 | { 541 | $this->mapConfig['geoMan']['cutPolygon'] = $this->evaluate($enable); 542 | return $this; 543 | } 544 | 545 | /** 546 | * Set the stroke color for drawings. 547 | * @param string $color 548 | * @return $this 549 | */ 550 | public function setColor(string $color): self 551 | { 552 | $this->mapConfig['geoMan']['color'] = $color; 553 | return $this; 554 | } 555 | 556 | /** 557 | * Set the fill color for drawings. 558 | * @param string $filledColor 559 | * @return $this 560 | */ 561 | public function setFilledColor(string $filledColor): self 562 | { 563 | $this->mapConfig['geoMan']['filledColor'] = $filledColor; 564 | return $this; 565 | } 566 | 567 | /** 568 | * Set custom HTML for marker icon 569 | * @param string $html 570 | * @return $this 571 | */ 572 | public function markerHtml(string $html): self 573 | { 574 | $this->mapConfig['markerHtml'] = $html; 575 | return $this; 576 | } 577 | 578 | /** 579 | * Set marker icon URL 580 | * @param string|null $url 581 | * @return $this 582 | */ 583 | public function markerIconUrl(?string $url): self 584 | { 585 | $this->mapConfig['markerIconUrl'] = $url; 586 | return $this; 587 | } 588 | 589 | /** 590 | * Set marker icon size 591 | * @param array $size 592 | * @return $this 593 | */ 594 | public function markerIconSize(array $size): self 595 | { 596 | $this->mapConfig['markerIconSize'] = $size; 597 | return $this; 598 | } 599 | 600 | /** 601 | * Set marker icon class name 602 | * @param string $className 603 | * @return $this 604 | */ 605 | public function markerIconClassName(string $className): self 606 | { 607 | $this->mapConfig['markerIconClassName'] = $className; 608 | return $this; 609 | } 610 | 611 | /** 612 | * Set marker icon anchor point 613 | * @param array $anchor 614 | * @return $this 615 | */ 616 | public function markerIconAnchor(array $anchor): self 617 | { 618 | $this->mapConfig['markerIconAnchor'] = $anchor; 619 | return $this; 620 | } 621 | 622 | /** 623 | * Setup function 624 | * @return void 625 | */ 626 | protected function setUp(): void 627 | { 628 | parent::setUp(); 629 | } 630 | } 631 | -------------------------------------------------------------------------------- /src/MapPickerServiceProvider.php: -------------------------------------------------------------------------------- 1 | name(static::$name) 27 | ->hasCommands($this->getCommands()) 28 | ->hasInstallCommand(function (InstallCommand $command): void { 29 | $command 30 | ->publishConfigFile() 31 | ->askToStarRepoOnGitHub('dotswan/filament-map-picker'); 32 | }); 33 | 34 | $configFileName = $package->shortName(); 35 | 36 | if (file_exists($package->basePath("/../config/{$configFileName}.php"))) { 37 | $package->hasConfigFile(); 38 | } 39 | 40 | if (file_exists($package->basePath('/../resources/lang'))) { 41 | $package->hasTranslations(); 42 | } 43 | 44 | if (file_exists($package->basePath('/../resources/views'))) { 45 | $package->hasViews(static::$viewNamespace); 46 | } 47 | } 48 | 49 | public function packageRegistered(): void 50 | { 51 | } 52 | 53 | public function packageBooted(): void 54 | { 55 | // Asset Registration 56 | FilamentAsset::register( 57 | $this->getAssets(), 58 | $this->getAssetPackageName() 59 | ); 60 | 61 | FilamentAsset::registerScriptData( 62 | $this->getScriptData(), 63 | $this->getAssetPackageName() 64 | ); 65 | 66 | // Icon Registration 67 | FilamentIcon::register($this->getIcons()); 68 | } 69 | 70 | protected function getAssetPackageName(): ?string 71 | { 72 | return 'dotswan/filament-map-picker'; 73 | } 74 | 75 | /** 76 | * @return array 77 | */ 78 | protected function getAssets(): array 79 | { 80 | return [ 81 | // AlpineComponent::make('map-picker', __DIR__ . '/../resources/dist/components/filament-map-picker.js'), 82 | Css::make('filament-map-picker-styles', __DIR__.'/../resources/dist/filament-map-picker.css'), 83 | Js::make('filament-map-picker-scripts', __DIR__.'/../resources/dist/filament-map-picker.js'), 84 | ]; 85 | } 86 | 87 | /** 88 | * @return array 89 | */ 90 | protected function getCommands(): array 91 | { 92 | return [ 93 | ]; 94 | } 95 | 96 | /** 97 | * @return array 98 | */ 99 | protected function getIcons(): array 100 | { 101 | return []; 102 | } 103 | 104 | /** 105 | * @return array 106 | */ 107 | protected function getRoutes(): array 108 | { 109 | return []; 110 | } 111 | 112 | /** 113 | * @return array 114 | */ 115 | protected function getScriptData(): array 116 | { 117 | return []; 118 | } 119 | 120 | /** 121 | * @return array 122 | */ 123 | protected function getMigrations(): array 124 | { 125 | return [ 126 | ]; 127 | } 128 | } 129 | --------------------------------------------------------------------------------