Source

services/predefined-views.js

import api, { getAPIV3Pooling } from '@/api'
import * as ramda from 'ramda'
import Ajv from 'ajv'

const latLngScaleFactor = 100000

const ajv = new Ajv({
  allErrors: true,
})
ajv.addKeyword('$isNotEmpty', {
  type: 'string',
  validate: function (schema, data) {
    return typeof data === 'string' && data.trim() !== ''
  },
  errors: false,
})

const predefinedViewCreateSchema = {
  type: 'object',
  properties: {
    id: { type: 'number' },
    name: { type: 'string', $isNotEmpty: true },
    xMin: { type: 'number' },
    xMax: { type: 'number' },
    yMin: { type: 'number' },
    yMax: { type: 'number' },
    default: { type: 'number' },
    //clientId: { type: 'number' },
    //userId: { type: 'number' },
  },
  required: ['name', 'xMin', 'xMax', 'yMin', 'yMax'],
}

export async function removePredefinedView(id) {
  if (!id) {
    throw new Error('id is required')
  }
  return (
    await api.v3.delete(
      `${api.APIUrls.APIV3_GET_GEORED_CLIENT_USER_MAP_VIEWS}/${id}`
    )
  ).data
}

/**
 * API stores Lat/Lng as integer (multiply by latLngScaleFactor and remove decimals)
 */
function getNormalizedLatLngValueForAPI(value) {
  //return Math.round(value * latLngScaleFactor)
  //return parseInt(value * latLngScaleFactor)
  //return parseInt(parseFloat(value * latLngScaleFactor).toFixed(0))

  value = parseFloat(value.toFixed(4)) //i.g 3 decimals is enough precision to target a parking lot

  //Similar to PHP intval
  return parseInt(value * latLngScaleFactor, 10)
}

export async function savePredefinedViewDefault(id) {
  const validateParams = ajv.compile({
    type: 'object',
    properties: {
      id: { type: 'number' },
    },
    required: ['id'],
  })
  const payload = {
    id,
    default: 1,
  }
  if (!validateParams(payload)) {
    //throw new Error(ajv.errorsText(validateParams.errors))
    console.error(ajv.errorsText(validateParams.errors))
    return false
  }
  return savePredefinedView(payload, {
    disableValidations: true,
    disableLatLngNormalization: true,
  })
}

export async function savePredefinedView(values, options = {}) {
  const validatePayload = ajv.compile(predefinedViewCreateSchema)
  const isValid = options.disableValidations === true || validatePayload(values)

  if (!isValid) {
    console.error({
      errors: ajv.errorsText(validatePayload.errors),
    })
    throw new Error(ajv.errorsText(validatePayload.errors))
    return false
  }

  if (options.disableLatLngNormalization !== true) {
    values = {
      ...values,
      xMin: getNormalizedLatLngValueForAPI(values.xMin),
      yMin: getNormalizedLatLngValueForAPI(values.yMin),
      xMax: getNormalizedLatLngValueForAPI(values.xMax),
      yMax: getNormalizedLatLngValueForAPI(values.yMax),
    }
  }

  let data

  if (values.id) {
    data = (
      await api.v3.patch(
        `${api.APIUrls.APIV3_GET_GEORED_CLIENT_USER_MAP_VIEWS}/${values.id}}`,
        values
      )
    ).data
  } else {
    data = (await api.v3.post(
      api.APIUrls.APIV3_GET_GEORED_CLIENT_USER_MAP_VIEWS,
      values
    ),
    values).data
  }

  return true
}

export async function getCenterLatLngFromPredefinedViewId(id) {
  if (!id) {
    return false
  }
  let res = await api.v3.get(
    api.APIUrls.APIV3_GET_GEORED_CLIENT_USER_MAP_VIEWS + '/' + id
  )
  let item = res.data
  if (item.id) {
    return getLatLngFromRectangle({
      topLeft: {
        lat: item.yMin / latLngScaleFactor,
        lng: item.xMin / latLngScaleFactor,
      },
      bottomRight: {
        lat: item.yMax / latLngScaleFactor,
        lng: item.xMax / latLngScaleFactor,
      },
    })
  } else {
    return false
  }
}

function getLatLngFromRectangle(rectangle) {
  const { topLeft, bottomRight } = rectangle
  const latDiff = topLeft.lat - bottomRight.lat
  const lngDiff = topLeft.lng - bottomRight.lng

  const midpointLat = topLeft.lat - latDiff / 2
  const midpointLng = topLeft.lng - lngDiff / 2

  return { lat: midpointLat, lng: midpointLng }
}

export async function fetchPredefinedViews(options = {}) {
  //return (await api.v3.get(api.APIUrls.APIV3_GET_GEORED_CLIENT_USER_MAP_VIEWS)).data

  return await getAPIV3Pooling({
    uri: `${api.APIUrls.APIV3_GET_GEORED_CLIENT_USER_MAP_VIEWS}?page=1&itemsPerPage=500`,
    transform:
      options.transform !== undefined
        ? options.transform
        : function normalizePredefinedViewItem(item) {
            return {
              ...ramda.pick(['id', 'userId', 'clientId', 'name'], item),
              xMin: item.xMin / latLngScaleFactor,
              xMax: item.xMax / latLngScaleFactor,
              yMin: item.yMin / latLngScaleFactor,
              yMax: item.yMax / latLngScaleFactor,
            }
          },
    payload: {
      properties: options.properties || [
        'id',
        'userId',
        'clientId',
        'name',
        'xMin',
        'yMin',
        'xMax',
        'yMax',
      ],
    },
    forcePayload: true,
  })
}

export async function fetchDefaultPredefinedView() {
  let views = await fetchPredefinedViews({
    transform: null,
    properties: ['id', 'default'],
  })
  return (
    views.find((v) => v.default === 1 || v.default === true || !!v.default)
      ?.id || null
  )
}