Vue.js cannot show page well - javascript

I could see a proper page after refreshing (F5)
I think that there is a problem to render markers
I get a position data to draw markers in map api from Json-server
To get a data, I'm using axios and Async Await function beforeFountain()
When I enter a page, I could not see markers
After refreshing this page (push F5), I could see markers
What I tried to resolve this problem
changed axios get functions, code sequence
Position data:
{
"manholes":[
{
"id":0,
"lat":37.55009275087953,
"lng":127.05067540273716,
"type":"point"
},
{
"id":1,
"lat":37.5501997640179,
"lng":127.04793121391802,
"type":"point"
}
]
}
Vue.js script:
data: () => ({
manholes: [],
}),
beforeMount () {
this.getManholeData()
},
mounted () {
window.kakao && window.kakao.maps
? this.initMap()
: this.addKakaoMapScript()
},
methods: {
// axios get function with async await
async getManholeData () {
try {
const baseURI = 'http://localhost:3000/manholes'
const response = await this.$axios.get(baseURI)
this.manholes = response.data
} catch (ex) {
console.log(ex)
}
},
initMap () {
// Map API config
var container = document.getElementById('map')
var options = {
center: new kakao.maps.LatLng(37.5500792286216, 127.0506923683668),
level: 3,
}
var map = new kakao.maps.Map(container, options)
var imageSrc = require('#/assets/manhole.png')
var imageSize = new kakao.maps.Size(32, 32)
var imageOption = { offset: new kakao.maps.Point(30, 30) }
var markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOption)
this.manholes.forEach(function (data) {
//use position data
console.log(data.lat)
console.log(data.lng)
var marker = new kakao.maps.Marker({
position: new kakao.maps.LatLng(data.lat, data.lng),
image: markerImage,
map: map,
clickable: true,
})
var iwContent = document.createElement('div')
iwContent.className = 'infowindow'
// iwContent.setAttribute('style', 'width:165px;text-align:center;padding:5px')
}
My problem:
My Goal:

Related

Why can't I load 3D models with Mapbox GL JS and Threebox with this Angular code

Can anyone help me understand how I need to structure my code for this mapbox w/ threebox project in Angular?
I can't figure out why this code:
this.tb.loadObj(options, function (model) {
var house = model.setCoords(origin);
this.tb.add(house);
});
is throwing the following error:
ERROR Error: Uncaught (in promise): ReferenceError: tb is not defined
ReferenceError: tb is not defined
Even after seeming to recognize tb as defined when I run console.log statements within this code block. But then this error shows up right afterwards and my 3D model never loads.
Here is the full code for the component, any advice on how to solve this issue would be appreciated:
import { Component, OnInit } from '#angular/core';
import { environment } from '../../../environments/environment';
import {Threebox} from 'threebox-plugin';
import * as M from 'mapbox-gl';
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
/// default settings
long = -122.4192;
lat = 37.7793;
map: M.Map;
style = 'mapbox://styles/mapbox/light-v10';
// data
source: any;
markers: any;
// render
tb: Threebox;
constructor() { }
ngOnInit(): void {
(M as any).accessToken = environment.mapbox.accessToken;
this.buildMap();
this.map.on('style.load', this.onLoad.bind(this));
this.map.on('load', (event) => {
/// register source
this.map.addSource('localdata', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [this.long, this.lat]
},
properties: {
title: 'Mapbox',
description: 'San Francisco, California'
}
}]
}
});
/// get source
this.source = this.map.getSource('localdata')
// markers
this.source._data.features.forEach((marker) => {
var lng = marker['geometry']['coordinates'][0]
var lat = marker['geometry']['coordinates'][1]
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
// make a marker for each feature and add to the map
new M.Marker({color: 'black'})
.setLngLat([lng, lat])
.addTo(this.map);
});
});
// Add map controls
this.map.addControl(new M.NavigationControl());
this.map.on('mousemove', function (e) {
document.getElementById('info').innerHTML =
// e.point is the x, y coordinates of the mousemove event relative
// to the top-left corner of the map
// e.lngLat is the longitude, latitude geographical position of the event
e.lngLat.lat.toFixed(6) + ', ' + e.lngLat.lng.toFixed(6) ;
});
};
// functions
buildMap() {
this.map = new M.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
zoom: 18,
center: [this.long, this.lat],
pitch: 60,
});
}
onLoad(){
this.map.addLayer({
id: 'house',
type: 'custom',
// renderingMode: '3d',
onAdd(map, mbxContext) {
this.tb = new Threebox(
map,
mbxContext,
{ defaultLights: true }
);
//starting location for both map and eventual sphere
var origin = [this.long, this.lat];
var options = {
// obj: 'src/assets/3d/house.gltf',
obj: 'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
type: 'gltf',
scale: 1,
units: 'meters',
rotation: { x: 90, y: 0, z: 0 } //default rotation
}
this.tb.loadObj(options, function (model) {
var house = model.setCoords(origin);
this.tb.add(house);
});
},
render: function (gl, matrix) {
this.tb.update();
}
});
};
}
tb needs to be global:
https://github.com/jscastro76/threebox/blob/master/docs/Threebox.md#threebox-instance
try:
(window as any).tb = new Threebox(map, mbxContext, {defaultLights: true});
and
window['tb'].loadObj(options, function (model) {
var house = model.setCoords(origin);
window['tb'].add(house);
});
It seems as window.tb solves that issue.
Initialise tb outside onAdd function.
this.tb = new Threebox(map, mbxContext, options);
window.tb = this.tb;
Now you can use either this.tb or window.tb because both are the same.

Map component occasionally throws an error: Uncaught (in promise), "H.map.DataModel#add (Argument #0 [object Object])"

I'm using Here Maps (Freemium plan) in a VueJs application that uses vue-router.
I made a Vue component for displaying a map which uses Routing to create an SVG path between 2 points following this articles:
https://developer.here.com/blog/showing-a-here-map-with-the-vue.js-javascript-framework
https://developer.here.com/documentation/map-image/dev_guide/topics/examples-routing-new-york-pois.html
I include Here Maps library ver 3.1 (CDN):
https://js.api.here.com/v3/3.1/mapsjs-service.js
https://js.api.here.com/v3/3.1/mapsjs-data.js
https://js.api.here.com/v3/3.1/mapsjs-ui.js
https://js.api.here.com/v3/3.1/mapsjs-mapevents.js
But the Vue component occasionally throws an error:
Uncaught (in promise) D {message: "H.map.DataModel#add (Argument #0 [object Object])"
The things I've noticed are:
If I reload the page sometimes it works fine and sometimes not
With certain route paths it works everytime
I've tried so hard to find the problem, no luck.
I think maybe it's the combination of vue-router and loading Here Maps library through CDN that causes this problem.
Is there a npm package for Here Maps out there?
Have you also experienced this issue?
Here is the code my map Vue component.
<template>
<div>
<div ref="map" class="here-map__map" />
</div>
</template>
<script>
export default {
data() {
return {
map: {},
coordinates: { lat: 46, lng: 12 },
platform: {},
waypoints: [],
markers: [],
icon: ''
}
},
props: {
property: {
zoom: 8,
markerIcon: "marker",
routing: {
route": {
color: "#ff815b",
width: 3
},
mode: {
type: "fastest",
transport: "truck",
traffic: "traffic:disabled"
}
}
},
appId: YOUR_APP_ID,
appCode: YOUR_APP_CODE
},
computed: {
route() {
return this.property.routing.route
},
routingParameters() {
return {
mode:
this.property.routing && this.property.routing.mode
? Object.values(this.property.routing.mode).join(';')
: ''
}
}
},
async created() {
await this.initMap()
},
beforeDestroy() {
this.map = null
},
methods: {
async initMap() {
this.platform = await new H.service.Platform({
apikey: YOUR_API_KEY
})
await this.getCoordinates()
this.createMap()
},
getCoordinates() {
if (this.property.lat && this.property.lng) {
this.coordinates = { lat: this.property.lat, lng: this.property.lng }
} else {
this.getGeoCoordinates()
}
},
getGeoCoordinates() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(pos => {
this.coordinates = {
lat: pos.coords.latitude,
lng: pos.coords.longitude
}
})
} else {
console.warn('This browser does not support geolocalization.')
}
},
calculateWaypoints() {
if (this.property.routing && this.property.routing.waypoints) {
this.waypoints = this.property.routing.waypoints.map((item, index) => {
this.routingParameters[`waypoint${index}`] = `geo!${item.lat},${
item.lng
}`
})
} else {
this.routingParameters.waypoint0 = this.shipment
? `geo!${this.shipment.from_address.lat},${
this.shipment.from_address.lng
}`
: null
this.routingParameters.waypoint1 = this.shipment
? `geo!${this.shipment.to_address.lat},${
this.shipment.to_address.lng
}`
: null
}
},
calculateRoute() {
this.platform.getRoutingService().calculateRoute(
this.routingParameters,
result => {
if (result.response.route) {
let route = result.response.route[0]
let lineString = new H.geo.LineString()
route.shape.forEach(function(point) {
let parts = point.split(',')
lineString.pushLatLngAlt(parts[0], parts[1])
})
let routeLine = lineString
? new H.map.Polyline(lineString, {
style: {
strokeColor: this.route.color || '#000000',
lineWidth: this.route.width || 2
}
})
: null
this.drawWaypoints(route)
let mapObjects = [...this.markers, routeLine]
this.map.addObjects(mapObjects)
this.map.getViewModel().setLookAtData({
bounds: routeLine.getBoundingBox()
})
}
},
error => {
console.log(error)
}
)
},
drawWaypoints(route) {
route.waypoint.map(({ mappedPosition }, index) => {
this.markers.push(
new H.map.Marker(
{
lat: mappedPosition.latitude,
lng: mappedPosition.longitude
},
{ icon: this.icon }
)
)
})
},
async createMap() {
this.icon = new H.map.Icon(`/${this.property.markerIcon}.svg`)
let defaultLayers = await this.platform.createDefaultLayers()
this.map = new H.Map(
this.$refs[this.ref],
defaultLayers.vector.normal.map,
{
zoom: this.property.zoom,
center: this.coordinates
}
)
let events = new H.mapevents.MapEvents(this.map)
let behavior = new H.mapevents.Behavior(events)
let ui = H.ui.UI.createDefault(this.map, defaultLayers)
this.calculateWaypoints()
this.calculateRoute()
window.addEventListener('resize', () => {
this.map.getViewPort().resize()
})
}
}
}
</script>
<style lang="scss">
.here-map {
&__map {
height: 400px;
margin: 0 auto;
}
}
</style>
Thank you in advance.
Yes, we have a blog how to implement HERE JS API into VueJs but basically we don't realize а technically support an integration of third part libraries/frameworks with HERE JS API.
But any way some recommendations:
Please include all HERE JS libraries (mapsjs-core.js, mapsjs-service.js etc.) into the <head> HTML element like in our examples https://developer.here.com/documentation/examples/maps-js
Please keep the description how to initialize the map in our blog https://developer.here.com/blog/showing-a-here-map-with-the-vue.js-javascript-framework. There is "Because the map is a DOM component, we need to wait until the components have rendered before we try to work with it, so we can’t do it in the created method. Instead we can use the mounted method". But in your code you use created method.
Keep in mind that HERE map container element should not be part of Virtual DOM of Vue

clear and add markers again using marker.remove()

My goal : I want to remove markers from the map and redraw it using setinterval() to update position on the map.
Expected results : remove markers and redraw it again every 4sec.
Actual results : old markers is not removed and new markers are added on it over and over .
Error Massage : there is no Error message to include .
I tried to check if marker is not null and if not null to remove marker from the map (this.marker.remove()) I tried this.marker.removeLayer(this.map) . loop over all markers and remove it one by one or set markers to null .nothing worked . down here i will include the code . i would be happy for any help . thanks in advance .
`` new Vue({
el: '#app',
data: {
/* Data properties will go here */
map: null,
tileLayer: null,
errored: false,
xxx: [],
selectedValue: null,
marker: null,
geocoder: null,
},
computed: {
onlyUnique() {
return [...new Set(this.xxx.map((city => city.location.name)))];
}
},
mounted() {
/* Code to run when app is mounted */ // when the Vue app is booted up, this is run automatically.
this.initMap();
this.getData();
setInterval(this.getData,4000);
},
methods: {
/* Any app-specific functions go here */
initMap() {
this.map = L.map('map', {
center: [20.0, 5.0],
minZoom: 2,
zoom: 2
});
this.tileLayer = L.tileLayer(
'https://cartodb-basemaps-{s}.global.ssl.fastly.net/rastertiles/voyager/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '© OpenStreetMap, © CARTO',
subdomains: ['a', 'b', 'c']
}
);
this.tileLayer.addTo(this.map);
},
onChange(event) {
this.selectedValue = event.target.options[event.target.options.selectedIndex].text;
this.geocoder = L.esri.Geocoding.geocodeService();
this.geocoder.geocode().text(this.selectedValue).run((error, response) => {
if (error) {
return;
}
this.map.fitBounds(response.results[0].bounds);
});
},
getData(){
axios
.get('url')
.then(response => {this.xxx= response.data}).catch( error =>{
// handle error
console.log("//////ErroR//////");
console.log(error);
this.errored = true;
});
setTimeout (this.drawMarker,500);
},
drawMarker(){
if (this.marker) {
console.log(this.marker);
this.marker.remove();
}
for (var i = 0; i < this.xxx.length; i++) {
this.marker = new L.marker([this.xxx[i].location.gps.coordinates[1],this.xxx[i].location.gps.coordinates[0]])
.bindPopup("hello")
.addTo(this.map);
}
}
},
});```
Probably a this context scope issue:
setTimeout (this.drawMarker.bind(this), 500)
See also Leaflet- marker click event works fine but methods of the class are undefined in the callback function
This is a classic JavaScript mistake.
this in JavaScript does not necessarily refer to your class instance object. It is the context of the function when it is called.

OverlappingMarkerSpiderfier is not defined (Vue.js)

I'm trying to implement the OverlappingMarkerSpiderfier for my Google Maps, and it works because my markers are able to "spiderfy" when I click on a marker.
My issue is that in my dev console on VS Code, ESLint is still giving me the error 'OverlappingMarkerSpiderfier' is not defined. I don't really know what the issue is since my markers are working as intended when I click on them. Below is a picture showing that OverlappingMarkerWorkers even though there is an error from ESLint:
I want to get rid of the error in case a future error arises because of it. I've searched for solutions, and many people have commented that OverlappingMarkerSpiderfier should be loaded after Google Maps load. I've done that, but the error still persists.
I load my Google Maps asynchronously; below is my .js file that loads the Google Maps and OverlappingMarkerSpiderfier:
import api_keys from './api_keys'
const API_KEY = api_keys.google_maps_api_key;
const CALLBACK_NAME = 'gmapsCallback';
let initialized = !!window.google;
let resolveInitPromise;
let rejectInitPromise;
const initPromise = new Promise((resolve, reject) => {
resolveInitPromise = resolve;
rejectInitPromise = reject;
});
export default function init() {
if (initialized) return initPromise;
initialized = true;
window[CALLBACK_NAME] = () => resolveInitPromise(window.google);
const script = document.createElement('script');
script.async = true;
script.defer = true;
script.src = `https://maps.googleapis.com/maps/api/jskey=${API_KEY}&callback=${CALLBACK_NAME}`;
script.onerror = rejectInitPromise;
document.querySelector('head').appendChild(script);
const spiderfier = document.createElement('script');
spiderfier.defer = true;
spiderfier.src = "https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier/1.0.3/oms.min.js";
spiderfier.onerror = rejectInitPromise;
document.querySelector('head').appendChild(spiderfier);
return initPromise;
}
The following is my GoogleMaps component. The OverlappingMarkerSpiderfier implementation is located within "watch":
<template>
<div id="google-map">
</div>
</template>
<script>
import gMaps from '../lib/gMaps.js'
export default {
name: 'GoogleMaps',
props: {
events: Array
},
data() {
return {
map: null,
locations: []
}
},
async mounted() {
try {
const google = await gMaps();
const geocoder = new google.maps.Geocoder();
this.map = new google.maps.Map(this.$el);
geocoder.geocode({ address: 'USA'}, (results, status) => {
if (status !== 'OK' || !results[0]) {
throw new Error(status);
}
this.map.setCenter(results[0].geometry.location);
this.map.fitBounds(results[0].geometry.viewport);
});
} catch (error) {
console.error(error);
}
},
watch: {
async events() { //creates markers for the map; data is from a 3rd party API that is handled by a different component
try {
const google = await gMaps();
var oms = new OverlappingMarkerSpiderfier(this.map, {
markersWontMove: true,
markersWontHide: true,
basicFormatEvents: true
})
for(let i = 0; i < this.events.length; i++) {
let marker = new google.maps.Marker({
position: {
lat: parseInt(this.events[i].latitude, 10),
lng: parseInt(this.events[i].longitude, 10)
},
map: this.map,
title: this.events[i].title
})
let iw = new google.maps.InfoWindow({
content: this.events[i].description || 'No description available.'
});
google.maps.event.addListener(marker, 'spider_click', function() {
iw.open(this.map, marker);
});
oms.addMarker(marker);
}
}
catch(error) {
console.error(error)
}
}
}
}
</script>
<style lang="scss" scoped>
#google-map {
width: auto;
height: 100vh;
}
</style>
try either 1 of these
this.$nexttick(()=>{
code in mounted hook....
})
check if window.google object is loaded and your map reference is available before instantiating OverlappingMarkerSpiderfier.

Using google map in Vue / Laravel

Trying to implement google map into Vue component. But having a hard time. Actually, there is no error. But no map also :) Okay, what I tried so far down below.
In laravel blade I set my api.
<script async defer src="https://maps.googleapis.com/maps/api/js?key={{env('GOOGLE_MAPS_API')}}&callback=initMap"></script>
Then in Vue component;
data() {
return {
mapName: "map",
//some other codes
}
},
mounted() {
this.fetchEstates();
},
methods: {
fetchEstates(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
// console.log(response);
this.estates = response.data.data;
//some other codes....
//some other codes....
},
computed: {
//some other functions in computed...
//
initMap: function(){
var options =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), options);
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: this.estates.price,
color: "#fff",
},
position: {
lat: this.estates.lat,
lng: this.estates.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
}
<div id="map"></div>
and last marker url id bind is in controller like this,
public function details($id)
{
$estates = allestates::where('id', $id)->first();
return view('pages.details', compact('estates'));
}
Do I missing something in Vue js? Thank you!
From our discussion in the comments, I realise that your issue is because this.estates is still not defined when initMap() is executed. Remember that you are using an asynchronous operation (via axios) to populate this.estates, so it is undefined at runtime. What you can do is:
Keep the map initialisation logic in initMap()
Move all the Google Map marker creation until after the axios promise has been resolved. You can abstract all that into another method, e.g. insertMarkers()
Also, remember that you need to define estates in the app/component data, otherwise it will not be reactive.
Here is an example:
data() {
return {
mapName: "map",
// Create the estate object first, otherwise it will not be reactive
estates: {}
}
},
mounted() {
this.fetchEstates();
this.initMap();
},
methods: {
fetchEstates: function(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
this.estates = response.data.data;
// Once estates have been populated, we can insert markers
this.insertMarkers();
//pagination and stuff...
});
},
// Iniitialize map without creating markers
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
},
// Helper method to insert markers
insertMarkers: function() {
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: this.estates.price,
color: "#fff",
},
position: {
lat: this.estates.lat,
lng: this.estates.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
}
},
Update: It also turns out that you have not addressed the issue of the data structure of this.estates. It appears that you are receiving an array from your endpoint instead of objects, so this.estates will return an array, and of course this.estates.lat will be undefined.
If you want to iterate through the entire array, you will have to use this.estates.forEach() to go through each individual estates while adding the marker, i.e.:
data() {
return {
mapName: "map",
// Create the estate object first, otherwise it will not be reactive
estates: {}
}
},
mounted() {
this.fetchEstates();
this.initMap();
},
methods: {
fetchEstates: function(page = 1) {
axios.get('/ajax', {
params: {
page
}}).then((response) => {
this.estates = response.data.data;
// Once estates have been populated, we can insert markers
this.insertMarkers();
//pagination and stuff...
});
},
// Iniitialize map without creating markers
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
var map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
},
// Helper method to insert markers
insertMarkers: function() {
// Iterate through each individual estate
// Each estate will create a new marker
this.estates.forEach(estate => {
var marker = new google.maps.Marker({
map: map,
icon: 'imgs/marker.png',
url: "/pages/estates.id",
label: {
text: estate.price,
color: "#fff",
},
position: {
lat: estate.lat,
lng: estate.lng
}
});
google.maps.event.addListener(marker, 'click', function () {
window.location.href = this.url;
});
});
}
},
From what I can see in the screenshot you posted, this.estates is an array of objects? If that's the case you need to iterate through the array using forEach
this.estates.forEach((estate, index) => {
console.log(estate.lat);
//handle each estate object here
});
or use the first item in the array like so this.estates[0].lat, if you're only interested in the first item.

Categories

Resources