Integrate EasyButton, Geoman with ngx-leaflet - leaflet

I am using raw leaflet.js in my Angular Application which depends on some leaflet plugins like EasyButton, Geoman, Distortable Image. ngx-leaflet looks cool and simple. So I've decided to migrate to ngx-leaflet. But I am sure if it is possible to integrate these plugins with the library. If so provide some guidance.

Answering my own question.
Here are the steps I followed to get geoman working with ngx-leaflet.
Install geoman npm i #geoman-io/leaflet-geoman-free. refer geoman github page
Import geoman css in styles.scss #import '../node_modules/#geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css';
Import geoman in the component import '#geoman-io/leaflet-geoman-free';
Done.
then use it like this
this.map.pm.addControls({
position: 'topleft',
drawCircle: false,
drawCircleMarker: false,
drawPolyline: true,
drawRectangle: false,
drawPolygon: true,
editMode: false,
dragMode: false,
cutPolygon: false,
removalMode: false,
drawMarker: false
});

I am trying this integration, but I cannot find any typifications for Geoman.
In my project, I use #asymmetrik/ngx-leaflet and follow the documentation to access the map reference.
I wrote a component and within the call onMapReady, map.pm stops the construction of my application.
I get this error: The property 'pm' does not exist in type 'Map'.
import { Component, OnInit } from '#angular/core';
import * as L from 'leaflet';
import '#geoman-io/leaflet-geoman-free';
#Component({
selector: 'app-main-map',
templateUrl: './main-map.component.html',
styleUrls: ['./main-map.component.css']
})
export class MainMapComponent implements OnInit {
aMarker = L.marker([ -23.95, -46.38 ], {
icon: L.icon({
iconSize: [ 25, 41 ],
iconAnchor: [ 13, 41 ],
iconUrl: 'assets/marker-icon.png',
shadowUrl: 'assets/marker-shadow.png'
})
});
constructor() { }
ngOnInit(): void {
}
onMapReady(map: L.Map) {
this.aMarker.addTo(map);
map.pm.addControls({
position: 'topleft',
drawCircle: false,
drawCircleMarker: false,
drawPolyline: true,
drawRectangle: false,
drawPolygon: true,
editMode: false,
dragMode: false,
cutPolygon: false,
removalMode: false,
drawMarker: false
});
}
}
I know it's not the best, but the temporary solution is to use bracket notation. It works on my Angular 10 project.
// The same code as above, but with bracket notation.
onMapReady(map: L.Map) {
this.aMarker.addTo(map);
map["pm"]["addControls"]({
position: 'topleft',
drawCircle: true,
drawCircleMarker: true,
drawPolyline: true,
drawRectangle: true,
drawPolygon: true,
editMode: true,
dragMode: true,
cutPolygon: true,
removalMode: true,
drawMarker: true
});
}
While I can't find a better solution or time to create typings for Geoman, I use it like this.
I hope it is useful to anyone.

This worked for me in Angular 8 project:
npm i #geoman-io/leaflet-geoman-free
In your component
import '#geoman-io/leaflet-geoman-free';
import 'style-loader!#geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css';

Related

Using leaflet.markercluster with a Nuxt 3 app

I'm using Leaflet with Nuxt3, TypeScript and Composition API on a production website.
As we're getting more and more markers, I'd like to use leaflet.markercluster but I can't get how to make it work properly
Here's my setup :
leaflet.client.ts
import {
LIcon,
LMap,
LMarker,
LPopup,
LTileLayer,
} from "#vue-leaflet/vue-leaflet";
import L from "leaflet";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component("LMap", LMap);
nuxtApp.vueApp.component("LTileLayer", LTileLayer);
nuxtApp.vueApp.component("LMarker", LMarker);
nuxtApp.vueApp.component("LIcon", LIcon);
nuxtApp.vueApp.component("LPopup", LPopup);
return {
provide: {
L,
},
};
});
Map.vue
<client-only>
<l-map
ref="locationsMap"
:min-zoom="leafletOptions.minZoom"
:max-zoom="leafletOptions.maxZoom"
:zoom-animation="true"
:zoom="leafletOptions.zoom"
:center="leafletOptions.center"
:useGlobalLeaflet="false"
:options="{ tap: false }"
#ready="onLeafletReady">
<l-tile-layer :url="leafletOptions.url"/>
<template v-for="location in locations"
:key="location.id">
<l-marker
:lat-lng="[location.attributes.lat, location.attributes.long]"
v-if="location.attributes.active">
<div v-if="location.attributes.lat && location.attributes.long">
<l-popup class="text-center flex flex-col gap-y-4">
...
</l-popup>
<l-icon>
...
</l-icon>
</div>
</l-marker>
</template>
</l-map>
...
</client-only>
<script setup lang="ts">
import {LIcon, LMap, LMarker, LPopup, LTileLayer} from "#vue-leaflet/vue-leaflet";
import "leaflet/dist/leaflet.css";
const leafletOptions = ref({
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
minZoom: 5,
maxZoom: 13,
zoom: 5.5,
map: null,
center: [47.040182, 2.536054],
bounds: null,
overlayLocation: false,
colors: ["#ED722E", "#F6BE00", "#979B0B", "#DA2C81"],
});
// Setup and api calls to get locations
</script>
package.json
{
...,
"depencencies": {
"#vue-leaflet/vue-leaflet": "^0.7.0",
"leaflet": "^1.9.3",
"leaflet.markercluster": "^1.5.3",
},
"devDependencies": {
"nuxt": "^3.0.0",
"typescript": "^4.9.4"
"#types/leaflet.markercluster": "^1.5.1",
}
}
The thing is, now I try to group my markers by adding leaflet.markercluster. So I added something like this :
leaflet.client.ts
...
import "leaflet.markercluster";
import "leaflet.markercluster/dist/MarkerCluster.css";
import "leaflet.markercluster/dist/MarkerCluster.Default.css";
export default defineNuxtPlugin((nuxtApp) => {
...
return {
provide: {
L,
},
};
});
But now I don't know what to do next. Using L.markerClusterGroup() as the official documentation says does not work as we get a 500 error for using a client-side method with ssr.
I also tried to directly import in my component with import :
Map.vue
import { MarkerClusterGroup } from 'leaflet.markercluster';
const markersGroup = ref(null);
...
const onLeafletReady = async () => {
markersGroup.value = new MarkerClusterGroup() // NOT WORKING
await nextTick();
leafletObject.value = locationsMap.value;
leafletReady.value = true;
leafletObject.value.addLayer(markersGroup.value)
}
But we got the same problem as using L.anyMethod() by getting a 500 error.
I saw that Sam85 on this question has the package installed, but that was not the same problem. :/
Has anyone ever tried to make it work with Nuxt 3 ?

Adding Placeholder

tinymce.init({
mode: "exact",
elements: "Grievance",
forced_root_block: "",
theme: "advanced",
paste_text_sticky: true,
paste_text_sticky_default: true,
plugins: "wordcount,paste,autolink,advlink,contextmenu,fullscreen,nonbreaking,template,inlinepopups,style",
//plugins: "wordcount,paste,autolink,advlink,fullscreen,nonbreaking,template,inlinepopups,style",
// Theme options
//theme_advanced_buttons1: "bold,italic,underline,strikethrough,|,undo,redo,|,justifyleft,justifycenter,justifyfull,|,link,unlink,|,fullscreen",
//theme_advanced_buttons2: "cut,copy,paste,|,cleanup,removeformat,|,bullist,numlist,outdent,indent,hr,charmap",
//theme_advanced_buttons1: "cut,copy,paste,removeformat,|,undo,redo,|,bullist,numlist,outdent,indent,|,formatselect,bold,italic,underline,forecolor,backcolor,|,justifyleft,justifycenter,justifyfull,|,link,unlink,|,fullscreen",
theme_advanced_buttons1: "cut,copy,paste,removeformat,|,undo,redo,|,bullist,numlist,outdent,indent,|,bold,italic,underline,|,fullscreen",
//theme_advanced_blockformats: 'p,h1,h2,h4,h4,h5,h6',
theme_advanced_toolbar_location: "top",
theme_advanced_toolbar_align: "left",
//theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing: true,
theme_advanced_path: false,
content_css: "/css/admin.css",
selector: 'textarea', // change this value according to your HTML
setup: function (editor) {
editor.on('init', function(){
if (tinymce.get('Text').getContent() == ''){
tinymce.get('Text').setContent("<p id='#Grievance'>Please put Case Information here</p>");
}
});
//and remove it on focus
editor.on('focus',function(){
$('iframe').contents().find('#Grievance').remove();
});
}
});
I am trying to add a Placeholder but I am getting "editor.on is not a function" so how do I add a place holder please without going to version 5 as that seems to be completely different. Thanks
I managed it in the end using Blur and Focus. Thanks

Mongoose Error: Cannot read property 'length' of undefined in NestJs

I am using nests framework and versions of mongodb and mongoose are as specified below.
Please refer to the screenshot for error in detail.
versions
"mongodb": "4.0.0",
"mongoose": "5.5.12",
Error Screenshot
User Document Module
import { Module } from '#nestjs/common';
import { UserDocumentsService } from './user-documents.service';
import { UserDocumentsController } from './user-documents.controller';
import { MongooseModule } from '#nestjs/mongoose';
import { UserDocumentsSchema } from './schema/user-documents.schema';
#Module({
imports: [
// showing error on this line
MongooseModule.forFeature([
{ name: 'UserDocument', schema: UserDocumentsSchema },
]),
],
controllers: [UserDocumentsController],
providers: [UserDocumentsService],
})
export class UserDocumentsModule {}
App.module.ts
#Module({
imports: [
MongooseModule.forRootAsync({
imports: [SharedModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.mongoDBName(),
useNewUrlParser: true,
useFindAndModify: false,
}),
inject: [ConfigService],
}),
UserDocumentsModule,
],
providers: [AppGateway],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {
consumer.apply(contextMiddleware).forRoutes('*');
}
}
UPDATE
I think there is something wrong with the mongoose imports in the schema file. It says "could not find declaration for module 'mongoose'".
I tried removing and reinstalling mongoose and it's types. But now it shows new error.
I tried solutions mentioned in this post:
Node.js heap out of memory
But this also didn't work for me.
I'm using Mac-M1 with 8GB config.
UPDATE
The issue has been resolved now. The project is running on node v10.24.1 and I was using node v16.6.2.
After downgrading node version using NVM, this issue is gone.
You'll have to pull SharedModule import off MongooseModule.
Try this:
#Module({
imports: [
MongooseModule.forRootAsync({
useFactory: async (configService: ConfigService) => ({
uri: configService.mongoDBName(),
useNewUrlParser: true,
useFindAndModify: false,
}),
inject: [ConfigService],
}),
UserDocumentsModule,
SharedModule
],
providers: [AppGateway],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {
consumer.apply(contextMiddleware).forRoutes('*');
}
}
It was because I was using a wrong version of node. The project was built on node v10.24.1 and I was using node v16.6.2.
After downgrading node version using NVM, I was able to fix this issue.

How can i prevent popup to be closed when i have fitSelectedRoutes:true options selected in the routingControl

I am using leaflet and leaflet routing machine control libraries.
When i am creating some route path i have the folllowing code:
this.routingControl = L['Routing'].control({
router: L['Routing'].osrmv1({
serviceUrl: `http://router.project-osrm.org/route/v1/`,
language: 'en',
profile: 'car'
}),
showAlternatives: false,
lineOptions: { styles: [{ color: '#4caf50', weight: 7 }] },
fitSelectedRoutes: true,
altLineOptions: { styles: [{ color: '#ed6852', weight: 7 }] },
show: false,
routeWhileDragging: true,
addWaypoints: false,
waypoints: [
L.latLng(clickedLat, clickedLng),
L.latLng(this.selectedCityZipCodeObject.longitude, this.selectedCityZipCodeObject.latitude)
],
createMarker: function (i: number, waypoint: any, n: number) {
return null;
}
});
Note: if i have
fitSelectedRoutes:false
then when i click on some marker,which should make route path until other marker the pop up is showed.
But if i have
fitSelectedRoutes:true
then when i click on the marker it show the popup. but the map zoom is changed to fit the route path in the center between the markers and i have smaller zoom which is done automatic from the library.
And then my pop up is closed when the zoom is automatically changed . How can i prevent this from happening ?
I found that everytime this code is triggered on the map it self when there are movements
this.map.on('zoomend', function(){
thatt.lastEvent.target.unbindPopup()
.bindPopup(`
<div><b>Dispatcher:</b></div>
`).openPopup();
});
i tried to get the last marker and to open the pop up and without success.
I also tried
that.lastEvent.target
.unbindPopup()
.bindPopup(`
<div><b>Dispatcher:</b> ${truckLocationObj?.dispatcher}</div>
<div><b>Dispatcher Email:</b> ${truckLocationObj?.dispatcher_email}</div>
<div><b>Truck #:</b> ${truckLocationObj?.truck}</div>
<div><b>ZIP</b> ${truckLocationObj?.available_zip} </div>
<div><b>City:</b> ${truckLocationObj?.available_city}</div>
<div class='red'><b>Distance:</b> ${distance} km to ${that.selectedCityZipCodeObject.city}, time: ${getHm}</div>
<div><b>Available on:</b> ${truckLocationObj?.available_date}</div>
`, {closePopupOnClick: false, autoClose: false, closeOnClick:false, autopanstart:false}).openPopup();
with addiional options on the pop up itself but also without success.
So fitSelectedRoutes - true makes something like fitting bounds of the two markers.
var corner1 = L.latLng(0,0);
var corner2 = L.latLng(39.310, -84.432);
let bounds = L.latLngBounds(corner1, corner2);
map.fitBounds(bounds, { padding: [50, 50] });
with this answer here the problem will be solved.
https://stackoverflow.com/questions/51953050/leaflet-markercluster-exempt-marker-from-clustering
https://jsfiddle.net/sghL4z96/65/

How to use Page Transition in NativeScript

I'm trying to use page transition from routerExtensions without success. (2.3.0)
I tried in js:
this.routerExtensions.navigate(
[
'myPage'
],
{
animated: true,
transition:
{
name: 'flip',
duration: 2000,
curve: 'linear'
}
}
);
and I tried in the xml:
<Button text="Goto myPage" [nsRouterLink]="['/myPage']" pageTransition="flip"></Button>
Both ways works as I navigate to "myPage" but without animations.
Is there a setting I need to change to "enable" the animations or am I missing something obvious?
With the provided context it's difficult to say exactly why it's not working.
All you should really need to do is:
Setup the components you're routing to:
app-routing.module.ts
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: '/home' },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent },
];
Inject the native router extensions in the component with your router-outlet
app.component.ts
import { RouterExtensions } from "#nativescript/angular/router";
#Component({
selector: "ns-app",
templateUrl: "./app.component.html"
})
export class AppComponent {
constructor(private routerExtensions: RouterExtensions) {}
public navigate(link: string): void {
this.routerExtensions.navigate([link], {
animated: true,
transition: { name: 'slide' }
});
}
}
And of course have a way to call this method
app.component.ts
<StackLayout>
<FlexboxLayout>
<Button text="Home" (tap)="navigate('home')"></Button>
<Button text="About" (tap)="navigate('about')"></Button>
<Button text="Contact" (tap)="navigate('contact')"></Button>
</FlexboxLayout>
<StackLayout>
<page-router-outlet actionBarVisibility="never"></page-router-outlet>
</StackLayout>
</StackLayout>
I've setup a working repo demonstrating this here https://github.com/gsavchenko/nativescript-page-transitions. These are also possible to achieve in angular not using nativescript native routing APIs.
Cheers,
let me know if there are further questions.
Have a look at the Groceries app by nativescript and its a great resource for all nativescript component - https://github.com/NativeScript/sample-Groceries. You can find the transition animation they have given in it.
Good Luck. If need any help ask.