What is the good way to create custom GeoJSON component - javascript

I Need help to create GeoJSON custom component from React-Leaflet
Write with React and React-Leaflet (last version both)
The code works when write in the Map component, but I want to import/export it to split code
import React from 'react';
import { withLeaflet, GeoJSON } from 'react-leaflet'
import L from 'leaflet'
class CustomGesJSON extends GeoJSON {
getStyle(feature) {
// some code
}
pointToLayer(feature, latlng) {
// some code
}
onEachFeature(feature, layer) {
// some code
}
createLeafletElement(opts) {
const CustomGesJSON = L.geoJSON.extend({
onAdd: (map) => {
this.getStyle = this.getStyle.bind(this);
this.pointToLayer = this.pointToLayer.bind(this);
this.onEachFeature = this.onEachFeature.bind(this);
return this ;
}
});
return new CustomGesJSON({ data: this.props.data });
}
}
function testlog(txt) {
// some code
}
export default withLeaflet(CustomGesJSON);
I've got a error message "GeoJSON is not a constructor"
Function and method (not show here) works, I just need help to make a proper inheritance
Other solution are welcome to
Thanks for your help

It is probable the "GeoJSON" object exported by "react-leaflet" is not an ES6 class, but the Leaflet L.GeoJSON "class".
You can use Leaflet own pre-ES6 class inheritance scheme, as described in the Leaflet class theory tutorial:
const MyCustomClass = GeoJSON.extend({
options: {
onEachFeature: myCustomDefaultFunction
// etc.
}
});
export default MyCustomClass;

Related

ES6 circular dependency. Implementing dependency hub

I found a lot of articles about circular dependencies and that it indicates the design/architecture flaws in the project. In most of the cases it can be fixed easily by slightly refactoring your classes.
In this example I'm trying to create a hub for dependencies. This is a library of classes that can be imported by different projects. The goal is to have getters in the main class that will return an instance of the dependency. This instance has to be a singleton and it's stored in the main class' dependencies that next time if someone calls a getter - it will return the same instance of that class.
Here is how code looks like:
// deps
import { DepA } from './dep-a';
import { DepB } from './dep-b';
const depsKey = '__MY_DEPS__';
class Deps {
dependencies = {};
get depA() {
return this.getDependency('depA', DepA);
}
get depB() {
return this.getDependency('depB', DepB);
}
bind(key, value) {
this.dependencies[key] = value;
}
getDependency(key, serviceClass) {
let service = this.dependencies[key];
if (!service && !!serviceClass) {
// if instance is not created yet, we instantiate the class and put instance in the dependencies
service = new serviceClass();
this.bind(key, service);
}
return service;
}
}
export const deps = (() => {
window[depsKey] = window[depsKey] || new Deps();
return window[depsKey];
})();
// dep-a
import { deps } from './deps';
export class DepA {
methodA() {
console.log(deps.depB);
}
}
// dep-b
import { deps } from './deps';
export class DepB {
methodB() {
console.log(deps.depA);
}
}
As you can see - this creates a circular dependency problem, since class Deps uses classes DepA and DepB in its getters to create an instance of those classes if it doesn't exist. And classes DepA and DepB use an instance of Deps to retrieve each other via its getters.
I hope this explanation is not very cumbersome.
Can anybody suggest the changes I need to make to get rid of the circular dependency here but to keep an idea of accessing singletons via Deps class (deps instance)?
My recommendation would be this:
a-singleton.js
import { DepA } from './dep-a';
let singleton;
export function getA() {
if (!singleton) {
singleton = new DepA();
}
return singleton;
}
b-singleton.js
import { DepB } from './dep-b';
let singleton;
export function getB() {
if (!singleton) {
singleton = new DepB();
}
return singleton;
}
then wherever you need these singletons, you can import the file and call the function to get the singleton.
Depending on what these classes do and how/when they reference eachother, you may also be able to directly do export default new DepA(), but depending on how they reference eachother, and how much they need to do at instantiation time, the lazy approach I've shown here can be better or necessary.

React-Leaflet offline storage of tiles

I've seen a common solution to storing tiles offline with Leaflet by using localforage etc like this:
const map = L.map("map-id");
const offlineLayer = L.tileLayer.offline('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', localforage, {
minZoom: 13,
maxZoom: 19,
crossOrigin: true
});
offlineLayer.addTo(map);
I'm attempting to do this with React-Leaflet. I've read that you would have to extend the
TileLayer class. However, extending does not seem to work with React-Leaflet v2. enable react-leaflet to use be usable offline
Has anyone come up with a solution to this. Other than not using react-leaflet and using leaflet directly?
I'm working on the same thing. Didn't look at v2 but in v3 I think something like this is what is needed:
import { LayerProps, createTileLayerComponent, updateGridLayer, withPane } from '#react-leaflet/core';
import { TileLayer as LeafletTileLayer, TileLayerOptions } from 'leaflet';
import TileLayerOffline from 'leaflet.offline';
import 'leaflet';
import L from 'leaflet'
import '#react-leaflet/core';
export interface TileLayerProps extends TileLayerOptions, LayerProps {
url: string;
}
export const TileLayerOfflineExtended = createTileLayerComponent<LeafletTileLayer, TileLayerProps>(
function createTileLayer({ url, ...options }, context) {
return {
instance: (L.tileLayer as any).offline(url, withPane(options, context)),
context
};
},
updateGridLayer
);
What I haven't figured out is getting the controls to work. Would be useful if someone posted it here and we can get it into a react-leaflet plugin npm

OpenLayers 6 - ES6 project structure

I'm working on a project using OpenLayers 6 in ES6 with Webpack.
It's my first real ES6 project and I want to make it organized (and a bit modular) but I'm struggling with the use of imports and exports.
Currently my structure is :
- all.js
- map/
- index.js
- gpx.js
The all.js file is the "entry point".
all.js
import 'ol/ol.css';
import map from './map/index';
import { vector as GPXvector } from './map/gpx';
map.addLayer(GPXvector);
map/index.js
import { Map, View } from 'ol';
import { OSM } from 'ol/source';
import { Tile as TileLayer } from 'ol/layer';
const map = new Map({
layers: [
new TileLayer({
source: new OSM()
})
],
target: 'map',
view: new View({
center: [1037749, 5135381],
zoom: 10
})
});
export { map as default };
map/gpx.js
import { Vector as VectorLayer } from 'ol/layer';
import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';
import { unByKey } from 'ol/Observable';
import VectorSource from 'ol/source/Vector';
import GPX from 'ol/format/GPX';
import map from './index.js'; // Is that good ??
const style = {
// [...] Some style here
};
const source = new VectorSource({
url: 'test.gpx',
format: new GPX()
});
var onChange = source.on('change', function() {
if (source.getState() == 'ready') {
map.getView().fit(source.getExtent()); // Access to "map" from "index.js" HERE
unByKey(onChange);
}
});
const vector = new VectorLayer({
source: source,
style: function (feature) {
return style[feature.getGeometry().getType()];
}
});
export { vector, source };
I want to access to the map instance (initialized in map/index.js) from the map/gpx.js file (see comment in source code).
But I feel like I am importing map from map/index.js inside all.js, which is importing map/gpx.js which himself also imports map from map/index.js.
It sounds to me like some kind of "loop" imports where it will be a mess to handle the order of imports for example when I'll get more files in my project.
Also if you have any advice for me to start properly with ES6 it's cool !
EDIT 1
I changed to something else to see if it allows more granularity.
all.js
import 'ol/ol.css';
import map from './ntrak/index';
import MyGPX from './ntrak/gpx';
const gpx = new MyGPX(map, 'test.gpx');
map/gpx.js
import { Vector as VectorLayer } from 'ol/layer';
import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';
import { unByKey } from 'ol/Observable';
import VectorSource from 'ol/source/Vector';
import GPX from 'ol/format/GPX';
const style = {
// [...] Some style here
};
const _onSourceChange = function(map, source) {
if (source.getState() == 'ready') {
map.getView().fit(source.getExtent());
unByKey(_onSourceChange);
}
}
export default class {
constructor(map, url, fit = true) {
this.map = map;
this.url = url;
this.fit = fit;
this.loadGPX();
}
loadGPX() {
this.source = new VectorSource({
url: this.url,
format: new GPX()
});
if (this.fit) {
this.source.on('change', () => _onSourceChange(this.map, this.source));
}
this.vector = new VectorLayer({
source: this.source,
style: function(feature) {
return style[feature.getGeometry().getType()];
}
});
this.map.addLayer(this.vector);
}
};
I think it's cool because it allows to get multiple GPX vectors on the same map instance.
But if I want to do more stuff that interacts with my GPX source or vector I will need to pass the instance everytime instead of just importing the GPX file directly.
What do you think?
You can use CircularDependencyPlugin for webpack to track such circular dependencies.
There is no circular dependency in your example, import map from './index.js'; // Is that good ?? is ok.
Your es6 code is fine to me, I see one var usage (var onChange = ...), you should replace that.

How to import a external function in a Vue component?

I'm a newbie in javascript and vue.js and I'm facing somme issue when trying to add a new function in an existing programme.
I have put my new function (with others) in a separate file:
export const MyFunctions = {
MyFunction: function(param) {
// Doing stuff
}
}
Then I import the file in the component file and calling my function :
<script>
import {MyFunctions} from "#/components/MyFunctions.js";
export default {
name:"Miniature",
computed: {
useMyFunction() {
MyFunction("Please do some stuff !");
}
}
}
</script>
When the component is used, I get an error message
[Vue warn]: Property or method "MyFunction" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I've read a lot of documentation and fail to understand why it doesn't work. Can anyone help me with this ??
You're exporting an object then in order to use the MyFunction you need to access to that function using dot notation, like this: MyFunctions.MyFunction("Please do some stuff !")
I made a working example for this use-case: https://codesandbox.io/s/62l1j19rvw
MyFunctions.js
export const MyFunctions = {
MyFunction: function(param) {
alert(param);
}
};
Component
<template>
<div class="hello">
{{msg}}
<button #click="handleClick">Click me</button>
</div>
</template>
<script>
import {MyFunctions} from "../MyFunctions.js";
export default {
name: "HelloWorld",
data() {
return {
msg: "Welcome to Your Vue.js App"
};
},
methods:{
handleClick: function(){
MyFunctions.MyFunction("Please do some stuff !");
}
}
};
</script>
You can just import your javascript files into .vue files as long as they are inside <script> tags. Since Vue.js is after all javascript, the first part where you should look at while debugging is if you have some kind of mistake in your syntax. From what I see, there is some confusion with the import and export statements, which could be quite complex at first!
Check MDN's Documentation specially under named exports:
In the module, we could use the following
// module "my-module.js"
function cube(x) {
return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
var graph = { /* nice big object */ }
export { cube, foo, graph };
This way, in another script, we could have:
import { cube, foo, graph } from 'my-module';
// Use your functions wisely
what you export is an object, and what you use is a field/method inside this object, so you need to use your function this way:
MyFunctions.MyFunction("Please do some stuff !");

Setting up a utility class and using it within a vue component

A vue application I am working on currently has lots of code redundancies relating to date functions. In an effort to reduce these redundancies, I'd like to create a utility class as shown below, import it and set it to a Vue data property within the component, so I can call the date functions within it.
I am not certain on the best way to implement this. The current implementation results in an error saying TypeError: this.dates is undefined and my goal is not only to resolve this error but create/utilize the class in the Vue environment using best standards.
Importing utility class
import Dates from "./utility/Dates";
...
Component
const contactEditView = Vue.component('contact-edit-view', {
data() {
return {
contact: this.myContact
dates: Dates
}
},
...
Dates.js
export default {
dateSmall(date) {
return moment(date).format('L');
},
dateMedium(date) {
return moment(date).format('lll');
},
dateLarge(date) {
return moment(date).format('LLL');
}
};
View
Date of Birth: {{ dates.dateMedium(contact.dob) }}
My suggestion for this is to use a plugin option in Vue. About Vue plugin
So you will crate a new folder called services, add file yourCustomDateFormater.js:
const dateFormater = {}
dateFormater.install = function (Vue, options) {
Vue.prototype.$dateSmall = (value) => {
return moment(date).format('L')
}
Vue.prototype.$dateMedium = (value) => {
return moment(date).format('lll')
}
}
In main.js:
import YourCustomDateFormater from './services/yourCustomDateFormater'
Vue.use(YourCustomDateFormater)
And you can use it anywhere, like this:
this.$dateSmall(yourValue)
Or, if you want to use mixin. Read more about mixin
Create a new file dateFormater.js
export default {
methods: {
callMethod () {
console.log('my method')
}
}
}
Your component:
import dateFormater from '../services/dateFormater'
export default {
mixins: [dateFormater],
mounted () {
this.callMethod() // Call your function
}
}
Note: "Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It’s also a good idea to ship them as Plugins to avoid duplicate application." - Vue documentation
dateUtilsjs
import moment from 'moment-timezone'
function formatDateTime(date) {
return moment.utc(date).format("M/D/yyyy h:mm A")
}
export { formatDateTime }
Component JS
...
import { formatDateTime } from '../utils/dateUtils'
...
methods: {
formatDateTime,
}
Used within component
{{ formatDateTime(date) }}

Categories