threeJS Nuxt, import syntax in head to resolve error (cannot use import statement outside a module, missing stack frame) - vue.js

I am trying to use ThreeJS in my NuxtJS app. I am trying to resolve an error while deploying to local host.
The issue is with the following two lines:
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
which throws a syntax error "cannot use import statement outside a module". "node:vm - Missing stack frames".
The full code in the script tag is attached below.
The problem will likely resolve if I adapt the code into the head section of export default, but I cannot find the appropriate syntax for this.
<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export default {
data() {
return {
camera: new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000),
scene: new THREE.Scene(),
renderer: new THREE.WebGLRenderer({
canvas: document.querySelector('#canvas')
}),
mesh: null
}
},
head() {
return {
script: [
{
async: true,
type: 'module',
src: 'https://unpkg.com/es-module-shims#1.3.6/dist/es-module-shims.js'
},
{
type: 'module',
imports: {
type: 'module',
three: 'https://unpkg.com/three#<version>/build/three.module.js'
}
},
]
}
},
mounted() {
this.init;
},
methods: {
init() {
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.camera.position.setZ(30);
this.renderer.render(this.scene, this.camera);
// add shapes//geometry //materials //mesh
const geometry = new THREE.PlaneGeometry(10, 3, 16, 100)
const material = new THREE.MeshBasicMaterial({ color: 0xFF6347, wireframe: true });
const plane = new THREE.Mesh(geometry, material);
const controls = new OrbitControls(this.camera, this.renderer.domElement);
// renderer.render( scene, camera);
// render
this.scene.add(plane)
// lighting
const pointLight = new THREE.PointLight(0xFFFFFF)
pointLight.position.set(5, 5, 5)
// pointLight.position.set(20,20,20)
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
this.scene.add(pointLight, ambientLight)
const lightHelper = new THREE.PointLightHelper(pointLight)
const gridHelper = new THREE.GridHelper(200, 50);
this.scene.add(lightHelper, gridHelper)
// animate
function animate() {
requestAnimationFrame(animate);
plane.rotation.x += 0.01;
plane.rotation.y += 0.005;
plane.rotation.z += 0.01;
controls.update();
this.renderer.render(this.scene, this.camera);
}
animate()
}
},
}
</script>

Related

The $store i is not defined on the instance but referenced during render in vue, why?

I need to create an instance and then return it as $el. nodeTemplate is a function in mounted hook. But i have an error that in VisualComponent $store is not defined on the instance but referenced during render.
import router from "../../router";
import store from "../../store";
...
const nodeTemplate = (n, parent, cyn) => {
// parent is the same as window
const Card = Vue.use(store).use(router).extend(Visual);
const instance = new Card({
propsData: {
vis: n.graphData ? n.graphData : {},
visualType: n.visualType ? n.visualType : {},
size: '',
className: cyn.classes(),
},
});
instance.$mount();
return instance.$el;
};
...
cy1.nodeHtmlLabel([
{
query: 'node',
tpl: function (n) {
n.el = nodeTemplate(n, parent, cy1.$(`#${n.id}`));
return n.el
}
}
])

Add more features to the feature layer

Here is a piece of code to add a polygon, its 'name' (PERMIT_AREA) and description to the feature layer. My problem is the drawn polygon is not applying to the layer. How we can add more details such as name and description to the layer? Can you please correct my code,
view.when(() => {
const polygonSymbol = {
type: "simple-fill", // autocasts as new SimpleFillSymbol()
color: [207, 34, 171, 0.5],
outline: {
// autocasts as new SimpleLineSymbol()
color: [247, 34, 101, 0.9],
}
};
const sketchViewModel = new SketchViewModel({
view: view,
layer: graphicsLayer,
polygonSymbol: polygonSymbol,
});
sketchViewModel.create("polygon", { mode: "hybrid" });
// Once user is done drawing a rectangle on the map
// use the rectangle to select features on the map and table
sketchViewModel.on("create", async (event) => {
if (event.state === "complete") {
// this polygon will be used to query features that intersect it
const geometries = graphicsLayer.graphics.map(function (graphic) {
return graphic.geometry
});
const queryGeometry = await geometryEngineAsync.union(geometries.toArray());
const query = {
geometry: queryGeometry,
outFields: ["*"],
attributes: {
PERMIT_AREA:'sample PERMIT_AREA'
}
};
console.log(query);
lyrpermitAreaUrl.queryFeatures(query).then(function (results) {
var lyr = results.features;
selectFeatures(lyr);
});
}
});
});
// This function is called when user completes drawing a rectangle
// on the map. Use the rectangle to select features in the layer and table
function selectFeatures(geometryabc) {
console.log(geometryabc);
// create a query and set its geometry parameter to the
// rectangle that was drawn on the view
lyrpermitAreaUrl.applyEdits({ addFeatures: [geometryabc] }).then((editsResult) => {
console.log(editsResult);
console.log(editsResult.addFeatureResults[0].objectId);
});
}
I have edited the code like provided below. Now I can apply user entered polygon to feature layer,
require(["esri/config",
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/layers/TileLayer",
"esri/layers/VectorTileLayer",
"esri/layers/GraphicsLayer",
"esri/widgets/Search",
"esri/widgets/Sketch/SketchViewModel",
"esri/geometry/geometryEngineAsync",
"esri/Graphic",
],
function (esriConfig, Map, MapView, FeatureLayer, TileLayer, VectorTileLayer, GraphicsLayer, Search, SketchViewModel, geometryEngineAsync, Graphic) {
esriConfig.apiKey = "AAPK3f43082c24ae493196786c8b424e9f43HJcMvP1NYaqIN4p63qJnCswIPsyHq8TQHlNtMRLWokqJIWYIJjga9wIEzpy49c9v";
const graphicsLayer = new GraphicsLayer();
const streetmapTMLayer = new TileLayer({
url: streetmapURL
});
const streetmapLTMLayer = new VectorTileLayer({
url: streetmapLebelsURL
});
const lyrwholeMeters = new FeatureLayer({
url: MetersWholeURL,
outFields: ["*"],
});
const lyrMeters = new FeatureLayer({
url: MetersURL,
outFields: ["*"],
});
const lyrpermitAreaUrl = new FeatureLayer({
url: PermitAreaURL,
outFields: ["*"],
});
console.log(lyrMeters);
const map = new Map({
basemap: "arcgis-topographic", // Basemap layer service
layers: [streetmapTMLayer, streetmapLTMLayer, lyrMeters, lyrwholeMeters, graphicsLayer]
});
const view = new MapView({
map: map,
center: [-95.9406, 41.26],
// center: [-118.80500, 34.02700], //Longitude, latitude
zoom: 16,
maxZoom: 21,
minZoom: 13,
container: "viewDiv", // Div element
});
view.when(() => {
const polygonSymbol = {
type: "simple-fill", // autocasts as new SimpleFillSymbol()
color: [207, 34, 171, 0.5],
outline: {
// autocasts as new SimpleLineSymbol()
color: [247, 34, 101, 0.9],
}
};
const sketchViewModel = new SketchViewModel({
view: view,
layer: graphicsLayer,
polygonSymbol: polygonSymbol,
});
sketchViewModel.create("polygon", { mode: "hybrid" });
// Once user is done drawing a rectangle on the map
// use the rectangle to select features on the map and table
sketchViewModel.on("create", async (event) => {
if (event.state === "complete") {
// this polygon will be used to query features that intersect it
const geometries = graphicsLayer.graphics.map(function (graphic) {
return graphic.geometry
});
const queryGeometry = await geometryEngineAsync.union(geometries.toArray());
var rawrings = queryGeometry.rings;
var rings = [];
console.log('rings here');
for (var i = 0; i < rawrings.length; i++) {
rings.push(rawrings[i]);
// graphics.push(graphic);
console.log(rawrings[i]);
}
console.log(rings[0]);
// Create a polygon geometry
const polygon = {
type: "polygon",
spatialReference: {
wkid: 102704, //102704,
},
rings: [rings[0]]
};
const simpleFillSymbol = {
type: "simple-fill",
color: [227, 139, 79, 0.8], // Orange, opacity 80%
outline: {
color: [255, 255, 255],
width: 1
}
};
const polygonGraphic = new Graphic({
geometry: polygon,
symbol: simpleFillSymbol,
});
graphicsLayer.add(polygonGraphic);
// const addEdits = {
// addFeatures: polygonGraphic
// };
console.log('here');
// console.log(addEdits);
// apply the edits to the layer
selectFeatures(polygonGraphic);
}
});
});
// This function is called when user completes drawing a rectangle
// on the map. Use the rectangle to select features in the layer and table
function selectFeatures(geometryabc) {
console.log(geometryabc);
// create a query and set its geometry parameter to the
// rectangle that was drawn on the view
lyrpermitAreaUrl.applyEdits({ addFeatures: [geometryabc] }).then((editsResult) => {
console.log(editsResult);
console.log(editsResult.addFeatureResults[0].objectId);
});
}
// search widget
const searchWidget = new Search({
view: view,
});
view.ui.add(searchWidget, {
position: "top-left",
index: 2
});
});
I have added the attributes like below,
var attr = {"NETSUITE_USERID":"test user","PERMIT_AREA":"test permit area","COMMENTS":"test comments"};
const polygonGraphic = new Graphic({
geometry: polygon,
symbol: simpleFillSymbol,
attributes: attr
});
If I understand then, you need a new Graphic with the sketched geometry and the attributes you query. Something like this might work,
sketchViewModel.on("create", async (event) => {
if (event.state === "complete") {
// remove the graphic from the layer. Sketch adds
// the completed graphic to the layer by default.
graphicsLayer.remove(event.graphic);
const newGeometry = event.graphic.geometry;
const query = {
geometry: newGeometry,
outFields: ["NAME", "DESCRIPTION"],
returnGeometry: false
};
lyrpermitAreaUrl.queryFeatures(query).then(function (results) {
if (!results.features) {
return;
}
const data = results.features[0].attributes;
const newGraphic = new Graphic({
geometry: newGeometry,
attributes: {
"NAME": data["NAME"],
"DESCRIPTION": data["DESCRIPTION"]
}
});
lyrpermitAreaUrl.applyEdits({ addFeatures: [newGraphic] }).then((editsResult) => {
console.log(editsResult);
});
});
}
});
NAME and DESCRIPTION are sample attributes name, you should use the ones you need.

ThreeJS component working in VueJS 2 but not 3

I'm upgrading my app to VueJS 3. I read that you could keep the same components. But I have an error in the console now, although I didn't change anything. Here is my component:
<template>
<v-container>
<div
#click="onClick"
#mousemove="onMouseMove"
id="menu3D"
style="background-color: transparent; position: fixed; left: 20px; width:15%; height:100%;">
</div>
<v-row class="text-center">
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
Accueil
</h2>
<v-row justify="center">
<p>
Client: {{ JSON.stringify(client)}}
</p>
<p>
Mouse: {{ JSON.stringify(mouse)}}
</p>
<p>
Container: {{ JSON.stringify(container)}}
</p>
</v-row>
</v-col>
</v-row>
</v-container>
</template>
<script>
import * as Three from 'three';
export default {
name: 'Accueil',
mounted() {
this.init();
},
methods: {
init() {
this.createScene();
this.createCamera();
this.userData.formes.forEach((x) => this.createShape(x));
this.addSpotlight(16777215);
this.addAmbientLight();
this.animate();
window.addEventListener('resize', this.onResize);
},
onResize() {
const container = document.getElementById('menu3D');
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.camera.aspect = container.clientWidth / container.clientHeight;
this.camera.updateProjectionMatrix();
},
createScene() {
this.renderer = new Three.WebGLRenderer({
antialias: true,
alpha: true,
});
const container = document.getElementById('menu3D');
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setClearColor(0xffffff, 0);
container.appendChild(this.renderer.domElement);
},
createCamera() {
const container = document.getElementById('menu3D');
this.camera = new Three.PerspectiveCamera(50,
container.clientWidth / container.clientHeight, 0.01, 1000);
this.camera.position.set(0, 5, 20);
this.camera.zoom = 1;
},
createShape(shape) {
const material = new Three.MeshStandardMaterial({
color: '#0000ff',
roughness: 1,
metalness: 0.5,
emissive: 0,
depthFunc: 3,
depthTest: true,
depthWrite: true,
stencilWrite: false,
stencilWriteMask: 255,
stencilFunc: 519,
stencilRef: 0,
stencilFuncMask: 255,
stencilFail: 7680,
stencilZFail: 7680,
stencilZPass: 7680,
});
switch (shape.nom) {
case 'Box': {
this.geometry = new Three.BoxBufferGeometry(1.8, 1.8, 1.8);
break;
}
case 'Sphere': {
this.geometry = new Three.SphereBufferGeometry(1, 8, 6, 0, 6.283185, 0, 3.141593);
break;
}
case 'Dodecahedron': {
this.geometry = new Three.DodecahedronBufferGeometry(1.2, 0);
break;
}
case 'Icosahedron': {
this.geometry = new Three.IcosahedronBufferGeometry(1.5, 0);
break;
}
default: {
return false;
}
}
this.mesh = new Three.Mesh(this.geometry, material);
this.mesh.name = shape.nom;
this.mesh.userData = shape.userData;
this.mesh.receiveShadow = true;
this.mesh.castShadow = true;
this.mesh.position.set(0, shape.userData.position.y, 0);
this.scene.add(this.mesh);
return true;
},
addSpotlight(color) {
const light = new Three.SpotLight(color, 2, 1000);
light.position.set(0, 0, 30);
this.scene.add(light);
},
addAmbientLight() {
const light = new Three.AmbientLight('#fff', 0.5);
this.scene.add(light);
},
verifForme(e) {
const t = this;
const elt = t.scene.getObjectByName(e);
t.intersects = t.raycaster.intersectObject(elt);
if (t.intersects.length !== 0) {
// s'il ne figure pas dans le tableau, on le met en premier
if (t.userData.souris.indexOf(e) < 0) {
t.userData.souris.unshift(e);
console.log(`${t.userData.souris[0]} survolé!`);
}
if (t.userData.souris[0] === e) {
const obj = t.intersects[0].object;
obj.material.color.set(`#${elt.userData.couleurs[1]}`);
obj.scale.set(obj.scale.x < 1.4
? obj.scale.x + t.VITESSE_ZOOM
: obj.scale.x, obj.scale.y < 1.4
? obj.scale.y + t.VITESSE_ZOOM
: obj.scale.y, obj.scale.z < 1.4
? obj.scale.z + t.VITESSE_ZOOM
: obj.scale.z);
obj.rotation.y += t.VITESSE_ROTATION / t.RALENTISSEMENT;
t.replacer(obj, obj.userData.position.y + obj.userData.decalage);
} else {
t.retrecir(e, elt);
}
} else {
if (t.userData.souris.indexOf(e) >= 0) {
t.userData.souris = t.userData.souris.filter((forme) => forme !== e);
}
t.retrecir(e, elt);
}
},
onClick(event) {
event.preventDefault();
if (this.userData.souris.length > 0) {
console.log(`${this.userData.souris[0]} cliqué!`);
} else {
console.log('clic dans le vide!');
}
},
onMouseMove(event) {
const container = document.getElementById('menu3D');
this.mouse.x = (event.offsetX / container.clientWidth) * 2 - 1;
this.mouse.y = -(event.offsetY / container.clientHeight) * 2 + 1;
this.client.clientX = event.clientX;
this.client.clientY = event.clientY;
this.container.width = container.clientWidth;
this.container.height = container.clientHeight;
// console.log(JSON.stringify(this.mouse))
},
replacer(e, py) {
// la ligne suivante est pour éviter les tremblements
if (Math.abs(e.position.y - py) < 0.05) { return true; }
let rhesus = 10 * this.VITESSE_DEPLACEMENT;
if (this.userData.souris[0] !== e.name) { rhesus *= 3; }
// console.log(e.name+': '+this.userData.souris[0]+' - '+rhesus)
if (e.position.y > py) { rhesus = -1; }
e.position.set(0, Math.trunc(10 * e.position.y + rhesus) / 10, 0);
return true;
},
retrecir(n, e) {
// on vérifie si le truc cliqué est dessus
let dec = 0;
const elt = this;
if ((elt.userData.souris.length > 0)
&& (elt.userData.formes.map((x) => x.nom).indexOf(n)
< elt.userData.formes.map((x) => x.nom).indexOf(elt.userData.souris[0]))) {
dec = Math.trunc(10
* e.parent.getObjectByName(elt.userData.souris[0]).userData.decalage
* 2.1) / 10;
}
e.material.color.set(`#${e.userData.couleurs[0]}`);
e.rotation.y += elt.VITESSE_ROTATION;
e.scale.set(e.scale.x > 1
? e.scale.x - elt.VITESSE_ZOOM : e.scale.x,
e.scale.y > 1
? e.scale.y - elt.VITESSE_ZOOM : e.scale.y,
e.scale.z > 1
? e.scale.z - elt.VITESSE_ZOOM : e.scale.z);
const newY = e.userData.position.y + dec;
if (e.position.y !== newY) {
elt.replacer(e, newY);
}
},
animate() {
const elt = this;
requestAnimationFrame(this.animate);
this.raycaster.setFromCamera(this.mouse, this.camera);
this.userData.formes.map((x) => x.nom).forEach((x) => elt.verifForme(x));
if (this.userData.souris.length > 0) {
document.body.style.cursor = 'pointer';
} else { document.body.style.cursor = 'default'; }
this.camera.updateProjectionMatrix();
this.renderer.render(this.scene, this.camera);
},
},
data: () => ({
container: { height: 0, width: 0 },
client: { clientX: 0, clientY: 0 },
scene: new Three.Scene(),
camera: null,
renderer: Three.WebGLRenderer,
mesh: new Three.Mesh(),
factor: 0,
mouse: new Three.Vector2(1, 1),
raycaster: new Three.Raycaster(),
intersects: [],
VITESSE_ROTATION: 0.05,
VITESSE_DEPLACEMENT: 0.1,
VITESSE_ZOOM: 0.05,
RALENTISSEMENT: 3,
userData: {
souris: [],
formes: [
{
nom: 'Box',
userData: {
position: {
x: 0,
y: 7.8,
z: 0,
},
couleurs: [
'aaaaaa',
'095256',
],
decalage: 0.5,
},
},
{
nom: 'Icosahedron',
userData: {
position: {
x: 0,
y: 5.5,
z: 0,
},
couleurs: [
'aaaaaa',
'087F8C',
],
decalage: 0.5,
},
},
{
nom: 'Dodecahedron',
userData: {
position: {
x: 0,
y: 3.1,
z: 0,
},
couleurs: [
'aaaaaa',
'5AAA95',
],
decalage: 0.4,
},
},
{
nom: 'Sphere',
userData: {
position: {
x: 0,
y: 1,
z: 0,
},
couleurs: [
'aaaaaa',
'86A873',
],
decalage: 0.2,
},
},
],
},
}),
};
</script>
And here is the error I have in the console with VueJS 3:
three.module.js?5a89:24471 Uncaught TypeError:
'get' on proxy: property 'modelViewMatrix' is a read-only and
non-configurable data property on the proxy target but the proxy did not
return its actual value (expected '#<Matrix4>' but got '[object Object]')
at renderObject (three.module.js?5a89:24471)
at renderObjects (three.module.js?5a89:24458)
at Proxy.WebGLRenderer.render (three.module.js?5a89:24258)
at animate (HelloWorld.vue?fdab:192)
If anyone has got a clue, thanks in advance...
It worked with Vue 2
Reason it worked fine with Vue 2 lies in the fact Vue 2 is using different reactivity system based on Object.defineProperty API.
The same API is used by THREE.js a lot to add some non-writable and non-configurable properties to it's data structures
When object with such property was passed to Vue (by declaring it inside data for example), Vue just skipped such property resulting in stored value/object being non-reactive (as Vue could not detect property access while rendering the component template)
Vue 3 proxies
Vue 3 is using new reactivity system base on ES6 proxies.
This is pretty new and even that a lot of effort has been put into developing and testing it, issues like this will arise as people start migrating (And I completely agree with #Serg - Vue 3 is still new and unless you have skill and time to "live on the edge" you should wait a bit before migrating from Vue 2)
This new reactivity system doesn't play well with non-writable non-configurable properties on objects - you can find minimal reproducible example in this sandbox
Imho it is a bug and is reported to Vue#next repo
sandbox uses composition API but that doesn't matter as using reactive() is the same as declaring your variables inside data() function (Vue just do it automatically for you)
Workarounds
As said before, problem is in reactivity system. I'm not an expert on THREE.js but from what I know it doesn't make much sense to put the THREE data structures into Vue reactivity system - all point of reactivity is to detect data changes and re-render template when needed. THREE has its own rendering system and is usually using single <canvas> HTML element so it makes no sense to trigger Vue re-render on THREE data structures change...
There are multiple ways to opt-out from Vue reactivity:
Use Object.freeze() on your objects. Not very useful in this case but good to know
Do not declare your variables in data() and assign the values in created()/mounted() hook (example bellow). You can assign them into component itself (this) if you need to access them in multiple methods or as a local variables (const/let) whenf you don't need it
When using Composition API, do not use reactive() on THREE data structures
NOTE: Even if they admit it is a bug, only way of fixing it is to leave such property and object it holds non-reactive (not putting Proxy around that object) so result will be same as opting-out of reactivity completely. But using this workaround also gives you faster and less memory hungry app as all the reactivity is not really that cheap
Example - creating non-reactive component properties
export default {
data() {
return {
};
},
mounted() {
this.init();
},
methods: {
init() {
this.scene = new THREE.Scene();
this.camera = new THREE.OrthographicCamera(...);
this.renderer = new THREE.WebGLRenderer({ ... })
this.geometry = new THREE.PlaneBufferGeometry( );
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
this.plane = new THREE.Mesh(this.geometry, material);
this.scene.add(this.plane);
this.renderer.render(this.scene, this.camera);
},
}
toRaw(vue3) - At this time, you can feel his strength !
You can use this method, to solve a series of these problems
If mesh/xxx is a ref variable
scene.add(toRaw(mesh.value))
renderer.value.render(toRaw(scene.value), camera.value);
I am using threejs + vue3 + pinia. Pinia was wrapping objects in Proxy too, but I need to pass 3d object to it sometimes (inside if other model). So I had a model like:
class SomeModel {
otherProp: 'some value',
graphicObject: new THREE.Object3D(),
}
The way I fixed this issue is by changing graphicObject prop to a function, that return 3d object, that was saved in other variable. It looks like this:
class SomeModel {
otherProp: 'some value',
constructor(graphicObject) {
this.graphicObject = () => graphicObject,
}
}
new SomeModel(new THREE.Object3D());
This way 3d object is hidden from Vue at all time, if you dont pass this object directly to any reactive variable. And the way you access it in other methods is by just calling this function like in example:
<script setup>
import { ref } from 'vue';
// You may just call constructor inside new SomeModel() if you want.
const graphicObject = new THREE.Object3D();
const someModel = ref(new SomeModel(graphicObject));
function changePosition(x, y, z) {
// should not emit errors, because 3d object is not reactive -
// it's just a value, returned from function.
someModel.value.graphicObject().position.set(x, y, z);
}
</script>

Markers are not shown on leaflet map

I am working on a task in which I have to show coordinates from an API.
Currently i am successfully able to fetch the data. But when I show it on the Map, Markers are not visible.
When i look in console the following errors appears:-
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined at eval
I have individually tried to pin point the source of error by putting console.log("done") at regular intervals and the exception occurs after the following line in my code:
this.map.addLayer(this.markers);
I have marked this line using comment in my code.
here's what i put in my html
<div id="mapApp">
<div class="container-fluid" style="padding: 0px; position:
relative; min-height: 90vh">
<div id="map" class="map"></div>
</div>
</div>
here's my Full JavaScript File
import Vue from 'vue'
import axios from 'axios'
import L from 'leaflet'
import googleMutant from 'leaflet.gridlayer.googlemutant'
import markercluster from 'leaflet.markercluster'
const baseUrl = 'http://127.0.0.1:8000/';
const vm = new Vue({
el: '#mapApp',
data: {
map: null,
tileLayer: null,
layers: [],
properties: [],
panState: 'closed',
foodIconUnselected: null,
foodIconSelected:null,
realEstateIconUnselected:null,
realEstateIconSelected:null,
zoomlevel:8,
firstLatitude:null,
firstLongitude:null,
roadMutant:null,
markers:null,
allPropertyLayer:null,
mcdPropertyLayer:null,
subwayPropertyLayer:null,
burgerkingPropertyLayer:null,
chickeriaPropertyLayer:null,
starbucksPropertyLayer:null,
kfcPropertyLayer:null,
spettacoloPropertyLayer:null,
holycowPropertyLayer:null,
query:'',
iconLabel:null,
labelName:null,
},
created(){
// execute the method to fetch propertis
let searchParams = new URLSearchParams(window.location.search);
let search_term_value = searchParams.get("search_term")
this.fetchPropertyData(search_term_value);
},
methods: {
initMap() {
this.$data.map = L.map('map').setView([this.$data.firstLatitude, this.$data.firstLongitude], this.$data.zoomlevel);
this.$data.roadMutant = L.gridLayer.googleMutant({
maxZoom: 24,
type: 'roadmap',
styles: /*removed on purpose to reduce size */,
}).addTo(this.$data.map);
L.control.zoom({
position:'topright'
}).addTo(this.$data.map);
this.$data.markers = L.markerClusterGroup({
iconCreateFunction: function (cluster) {
var markers = cluster.getAllChildMarkers();
var html = '<div class="marker-cluster">' + markers.length + '</div>';
return L.divIcon({html: html, className: 'mycluster', iconSize: L.point(40, 40)});
},
spiderfyOnMaxZoom: false, showCoverageOnHover: false, zoomToBoundsOnClick: true,
});
this.$data.allPropertyLayer = new L.FeatureGroup();
this.$data.mcdPropertyLayer = new L.FeatureGroup();
this.$data.subwayPropertyLayer = new L.FeatureGroup();
this.$data.burgerkingPropertyLayer = new L.FeatureGroup();
this.$data.chickeriaPropertyLayer = new L.FeatureGroup();
this.$data.starbucksPropertyLayer = new L.FeatureGroup();
this.$data.kfcPropertyLayer = new L.FeatureGroup();
this.$data.spettacoloPropertyLayer = new L.FeatureGroup();
this.$data.holycowPropertyLayer = new L.FeatureGroup();
console.log("Done Till Here");
this.fillDataInMap();
},
fillFirstData(){
this.$data.firstLatitude = this.$data.properties[0].latitude;
this.$data.firstLongitude = this.$data.properties[0].longitude;
},
fetchPropertyData(search_term_value){
const self = this;
axios.get(baseUrl + 'api/search/property', {
params:{
'search_term': search_term_value
}
}).then(function (response) {
self.$data.properties = response.data.search_results.data;
self.fillFirstData();
self.initMap();
}).catch(function (error) {
alert(error.response.data);
console.log(error);
})
},
fillDataInMap(){
for(let i =0;i<this.$data.properties.length;i++){
this.$data.iconLabel = this.getPropertyIconLabelUnSelected(this.$data.properties[i]);
this.$data.labelName = this.getPropertyLabelName(this.$data.properties[i]);
let marker = L.marker(new L.LatLng(this.$data.properties[i].latitude, this.$data.properties[i].longitude), {icon: this.$data.iconlabel, id: i+1});
marker.bindTooltip(this.$data.labelName, {permanent: true, direction: 'auto', className: 'map-icon-label',});
marker.bindPopup(this.$data.properties[i].address);
marker.title = this.$data.properties[i].id;
marker.on('click', function (e) {
//showDetails(e, e.target.title);
//changeMarkerIconOnClick(e);
});
marker.on('mouseover',function (e) {
e.target.openPopup();
});
marker.on('mouseout',function (e) {
e.target.closePopup();
});
this.$data.allPropertyLayer.addLayer(marker);
this.addMarkerToRespectiveLayer(this.$data.properties[i].property_sub_type,marker);
}
this.$data.markers.addLayer(this.$data.allPropertyLayer);
Next line throws the exception
this.map.addLayer(this.markers);
},
searchWithQuery(){
this.fetchPropertyData(this.$data.query)
},
showPropertyDetails(id) {
alert('property id is: ' + id)
},
}
});
I found the reason why it was causing exception. I declared variable for custom markers but somehow forgot to initialise them. After removing the Custom markers, markers show on the map but then i am unable to interact with map. It remains static like an image.

fireEvent on SegmentedControlIOS

I am using react-native-testing-library - https://callstack.github.io/react-native-testing-library/docs/getting-started
I have a <SegmentedControlIOS> - https://facebook.github.io/react-native/docs/segmentedcontrolios
I want to pres the first segment. I am doing this:
const testID = "SegmentedControl";
const stub = jest.fn();
const values = [{ label: "foo" }];
const { getByTestId } = render(
<SegmentedControlIOS values={['foo', 'bar']} onChange={stub} testID={testID} />
);
expect(() => {
getByTestId(testID);
}).not.toThrow();
fireEvent(getByTestId(testID), "change ", {
nativeEvent: {
value: values[0],
selectedSegmentIndex: 0,
},
});
However I get the error:
No handler function found for event: "change "
Screenshot below. Anyone know how to press different segments in <SegmentedControlIOS>?
fireEvent(element: ReactTestInstance, eventName: string, ...data:
Array): void
The change function is located in the fireEvent object. Here's how to use it:
Version 5 or later:
fireEvent.change(getByTestId(testID), { target: { value: values[0],selectedSegmentIndex: 0 } });
Version 5 or before:
const input = getByTestId(testID);
input.value = values[0];
input.selectedSegmentIndex = 0;
fireEvent.change(input);
If you want to check the onChange function of SegmentedControlIOS,
using fireEvent with native events that aren't already aliased by the fireEvent api.
// you can omit the `on` prefix
fireEvent(getByTestId(testID), 'onChange');
A solution was posted here, I didn't try it yet, but it looks more right I think - https://github.com/callstack/react-native-testing-library/issues/220#issuecomment-541067962
import React from "react";
import { SegmentedControlIOS } from "react-native";
import { fireEvent, render } from "react-native-testing-library";
const testID = "SegmentedControl";
const stub = jest.fn();
const values = [{ label: "foo" }];
const { getByTestId } = render(
<SegmentedControlIOS
values={["foo", "bar"]}
onChange={stub}
testID={testID}
/>,
);
it("sends events", () => {
fireEvent(getByTestId(testID), "onChange", {
nativeEvent: {
value: values[0],
selectedSegmentIndex: 0,
},
});
});