diff --git a/src/app/api.js b/src/app/api.js index 271f0df44..4896ec724 100644 --- a/src/app/api.js +++ b/src/app/api.js @@ -35,6 +35,25 @@ import GUI from 'services/gui'; //MIXINS import Mixins from 'mixins'; +import { reverseGeometry } from 'utils/reverseGeometry'; +import { getExtentForViewAndSize } from 'utils/getExtentForViewAndSize'; +import { createPolygonLayerFromBBox } from 'utils/createPolygonLayerFromBBox'; +import { getLengthMessageText } from 'utils/getLengthMessageText'; +import { needUseSphereMethods } from 'utils/needUseSphereMethods'; +import { transformMeterLength } from 'utils/transformMeterLength'; +import { createMeasureTooltip } from 'utils/createMeasureTooltip'; +import { formatMeasure } from 'utils/formatMeasure'; +import { getCurrentMapUnit } from 'utils/getCurrentMapUnit'; +import { getAreaMessageText } from 'utils/getAreaMessageText'; +import { transformMeterArea } from 'utils/transformMeterArea'; +import { removeMeasureTooltip } from 'utils/removeMeasureTooltip'; +import { setMeasureTooltipStatic } from 'utils/setMeasureTooltipStatic'; +import { getMetersFromDegrees } from 'utils/getMetersFromDegrees'; +import { getDPI } from 'utils/getDPI'; +import { getResolutionFromScale } from 'utils/getResolutionFromScale'; +import { getScaleFromResolution } from 'utils/getScaleFromResolution'; +import { mergeOptions } from 'utils/mergeOptions'; + const G3WObject = require('core/g3wobject'); const utils = require('utils'); const geoutils = require('utils/geo'); @@ -96,7 +115,6 @@ const PickCoordinatesInteraction = require('g3w-ol/interactions/pickcoordinatesi const DeleteFeatureInteraction = require('g3w-ol/interactions/deletefeatureinteraction'); const AreaInteraction = require('g3w-ol/interactions/areainteraction'); const LengthInteraction = require('g3w-ol/interactions/lengthinteraction'); -const g3wolutils = require('utils/ol'); module.exports = { @@ -250,7 +268,26 @@ module.exports = { } }, controls: {}, - utils: g3wolutils + utils: { + merge: mergeOptions, + getExtentForViewAndSize, + createPolygonLayerFromBBox, + reverseGeometry, + getScaleFromResolution, + getResolutionFromScale, + getDPI, + getMetersFromDegrees, + needUseSphereMethods, + getLengthMessageText, + getAreaMessageText, + formatMeasure, + createMeasureTooltip, + getCurrentMapUnit, + transformMeterLength, + transformMeterArea, + removeMeasureTooltip, + setMeasureTooltipStatic, + }, }, // G3W-SUITE debug info diff --git a/src/app/constant.js b/src/app/constant.js index 2eaa5a1a0..9a6801785 100644 --- a/src/app/constant.js +++ b/src/app/constant.js @@ -620,10 +620,28 @@ export const LOGO_GIS3W = 'images/logo_gis3w_156_85.png'; */ export const SPATIAL_METHODS = ['intersects', 'within']; +/** + * DPI96 + * + * DOTS_PER_INCH = ol.has.DEVICE_PIXEL_RATIO * 96; + * + * @since 3.10.0 + */ +export const DOTS_PER_INCH = 96; + +/** + * @since 3.10.0 + */ +export const INCHES_PER_UNIT = { + m: 39.37, + degrees: 4374754 +}; + export default { APP_VERSION, API_BASE_URLS, DEFAULT_EDITING_CAPABILITIES, + DOTS_PER_INCH, DOWNLOAD_FORMATS, FILTER_OPERATORS, FILTER_EXPRESSION_OPERATORS, @@ -631,6 +649,7 @@ export default { G3W_FID, GEOMETRY_FIELDS, GEOMETRY_TYPES, + INCHES_PER_UNIT, LIST_OF_RELATIONS_TITLE, LIST_OF_RELATIONS_ID, LOCAL_ITEM_IDS, diff --git a/src/app/core/layers/mixins/geo.js b/src/app/core/layers/mixins/geo.js index 26ff652d5..580de75c4 100644 --- a/src/app/core/layers/mixins/geo.js +++ b/src/app/core/layers/mixins/geo.js @@ -5,11 +5,11 @@ * @since 3.9.0 */ -import GUI from 'services/gui'; -import ApplicationService from 'services/application'; +import GUI from 'services/gui'; +import ApplicationService from 'services/application'; +import { getScaleFromResolution } from 'utils/getScaleFromResolution'; const Projections = require('g3w-ol/projection/projections'); -const { getScaleFromResolution } = require('utils/ol'); const { createFeatureFromFeatureObject } = require('utils/geo'); const { XHR, sanitizeUrl } = require('utils'); diff --git a/src/app/core/layers/providersfactory.js b/src/app/core/layers/providersfactory.js index 31839742e..209474aec 100644 --- a/src/app/core/layers/providersfactory.js +++ b/src/app/core/layers/providersfactory.js @@ -2,6 +2,8 @@ import ApplicationState from 'store/application-state'; import RelationsService from 'services/relations'; import { QUERY_POINT_TOLERANCE } from 'constant'; import { QgsFilterToken } from 'core/layers/utils/QgsFilterToken'; +import { getDPI } from 'utils/getDPI'; +import { getExtentForViewAndSize } from 'utils/getExtentForViewAndSize'; import { get_legend_params } from 'utils/get_legend_params'; const G3WObject = require('core/g3wobject'); @@ -15,12 +17,11 @@ const { handleQueryResponse } = require('utils/geo'); const Parsers = require('utils/parsers'); const { t } = require('core/i18n/i18n.service'); const Feature = require('core/layers/features/feature'); -const geoutils = require('utils/ol'); const Filter = require('core/layers/filter/filter'); const GETFEATUREINFO_IMAGE_SIZE = [101, 101]; -const DPI = geoutils.getDPI(); +const DPI = getDPI(); const is_defined = d => undefined !== d; @@ -505,7 +506,7 @@ const Providers = { ? layers.map(layer => layer.getWMSInfoLayerName()).join(',') : this._layer.getWMSInfoLayerName(); - const extent = geoutils.getExtentForViewAndSize(coordinates, resolution, 0, size); + const extent = getExtentForViewAndSize(coordinates, resolution, 0, size); const is_map_tolerance = ('map' === query_point_tolerance.unit); diff --git a/src/app/g3w-ol/controls/areacontrol.js b/src/app/g3w-ol/controls/areacontrol.js index 9f9d65963..7a20d39ae 100644 --- a/src/app/g3w-ol/controls/areacontrol.js +++ b/src/app/g3w-ol/controls/areacontrol.js @@ -1,4 +1,5 @@ -const utils = require('utils/ol'); +import { mergeOptions } from 'utils/mergeOptions'; + const AreaIteraction = require('g3w-ol/interactions/areainteraction'); const MeasureControl = require('g3w-ol/controls/measurecontrol'); @@ -9,7 +10,7 @@ const AreaControl = function(options={}) { clickmap: true, // set ClickMap interactionClass: AreaIteraction }; - options = utils.merge(options, _options); + options = mergeOptions(options, _options); MeasureControl.call(this, options); }; diff --git a/src/app/g3w-ol/controls/basequerypolygoncontrol.js b/src/app/g3w-ol/controls/basequerypolygoncontrol.js index 364e6cb88..513073505 100644 --- a/src/app/g3w-ol/controls/basequerypolygoncontrol.js +++ b/src/app/g3w-ol/controls/basequerypolygoncontrol.js @@ -3,9 +3,9 @@ * @since v3.8 */ import { SPATIAL_METHODS } from 'app/constant'; +import { mergeOptions } from 'utils/mergeOptions'; const InteractionControl = require('g3w-ol/controls/interactioncontrol'); -const { merge } = require('utils/ol'); const { Geometry } = require('utils/geo'); const VALIDGEOMETRIES = Geometry.getAllPolygonGeometryTypes(); @@ -32,7 +32,7 @@ const BaseQueryPolygonControl = function(options = {}) { onhover: true }; - options = merge(options, default_options); + options = mergeOptions(options, default_options); options.geometryTypes = VALIDGEOMETRIES; diff --git a/src/app/g3w-ol/controls/lengthcontrol.js b/src/app/g3w-ol/controls/lengthcontrol.js index 3fb0c9f48..90d833eb9 100644 --- a/src/app/g3w-ol/controls/lengthcontrol.js +++ b/src/app/g3w-ol/controls/lengthcontrol.js @@ -1,6 +1,7 @@ -const utils = require('utils/ol'); +import { mergeOptions } from 'utils/mergeOptions'; + const LenghtIteraction = require('g3w-ol/interactions/lengthinteraction'); -const MeasureControl = require('g3w-ol/controls/measurecontrol'); +const MeasureControl = require('g3w-ol/controls/measurecontrol'); const LengthControl = function(options={}) { const _options = { @@ -10,7 +11,7 @@ const LengthControl = function(options={}) { interactionClass: LenghtIteraction }; - options = utils.merge(options,_options); + options = mergeOptions(options,_options); MeasureControl.call(this, options); }; diff --git a/src/app/g3w-ol/controls/querycontrol.js b/src/app/g3w-ol/controls/querycontrol.js index 654b620f3..26bf89451 100644 --- a/src/app/g3w-ol/controls/querycontrol.js +++ b/src/app/g3w-ol/controls/querycontrol.js @@ -1,9 +1,9 @@ -import GUI from 'services/gui'; -import ProjectsRegistry from 'store/projects'; +import GUI from 'services/gui'; +import ProjectsRegistry from 'store/projects'; import DataRouterService from 'services/data'; +import { mergeOptions } from 'utils/mergeOptions'; const { throttle } = require('utils'); -const utils = require('utils/ol'); const InteractionControl = require('g3w-ol/controls/interactioncontrol'); const PickCoordinatesInteraction = require('g3w-ol/interactions/pickcoordinatesinteraction'); @@ -17,7 +17,7 @@ const QueryControl = function(options = {}){ interactionClass: PickCoordinatesInteraction, }; - options = utils.merge(options, _options); + options = mergeOptions(options, _options); InteractionControl.call(this, options); }; diff --git a/src/app/g3w-ol/controls/resetcontrol.js b/src/app/g3w-ol/controls/resetcontrol.js index eeddcd207..b3769a9ba 100644 --- a/src/app/g3w-ol/controls/resetcontrol.js +++ b/src/app/g3w-ol/controls/resetcontrol.js @@ -1,4 +1,5 @@ -const utils = require('utils/ol'); +import { mergeOptions } from 'utils/mergeOptions'; + const InteractionControl = require('g3w-ol/controls/interactioncontrol'); const ResetControl = function(options){ @@ -9,7 +10,7 @@ const ResetControl = function(options){ tipLabel: "Pan", label: "\ue901" }; - options = utils.merge(options,_options); + options = mergeOptions(options,_options); InteractionControl.call(this,options); }; diff --git a/src/app/g3w-ol/controls/scalecontrol.js b/src/app/g3w-ol/controls/scalecontrol.js index 2495a72bb..b5ceda2ee 100644 --- a/src/app/g3w-ol/controls/scalecontrol.js +++ b/src/app/g3w-ol/controls/scalecontrol.js @@ -1,6 +1,8 @@ -const {t} = require('core/i18n/i18n.service'); -const { getResolutionFromScale, getScaleFromResolution } = require('utils/ol'); -import { PRINT_SCALES } from 'app/constant'; +import { PRINT_SCALES } from 'app/constant'; +import { getResolutionFromScale } from 'utils/getResolutionFromScale'; +import { getScaleFromResolution } from 'utils/getScaleFromResolution'; + +const { t } = require('core/i18n/i18n.service'); const ScaleControl = function(options= {}) { this.isMobile = options.isMobile || false; diff --git a/src/app/g3w-ol/controls/streetviewcontrol.js b/src/app/g3w-ol/controls/streetviewcontrol.js index b2e494181..731d54376 100644 --- a/src/app/g3w-ol/controls/streetviewcontrol.js +++ b/src/app/g3w-ol/controls/streetviewcontrol.js @@ -1,10 +1,10 @@ import ApplicationState from 'store/application-state'; -import GUI from 'services/gui'; +import GUI from 'services/gui'; +import { mergeOptions } from 'utils/mergeOptions'; -const { XHR } = require('utils'); -const utils = require('utils/ol'); -const StreetViewComponent = require('gui/streetview/vue/streetview'); -const InteractionControl = require('g3w-ol/controls/interactioncontrol'); +const { XHR } = require('utils'); +const StreetViewComponent = require('gui/streetview/vue/streetview'); +const InteractionControl = require('g3w-ol/controls/interactioncontrol'); const PickCoordinatesInteraction = require('g3w-ol/interactions/pickcoordinatesinteraction'); const GoogleStreetViewApiUrl = `https://maps.googleapis.com/maps/api/`; @@ -73,7 +73,7 @@ const StreetViewControl = function(options={}) { } }); - options = utils.merge(options,_options); + options = mergeOptions(options, _options); InteractionControl.call(this,options); }; diff --git a/src/app/g3w-ol/interactions/measureinteraction.js b/src/app/g3w-ol/interactions/measureinteraction.js index 0b561c588..7b0a655f5 100644 --- a/src/app/g3w-ol/interactions/measureinteraction.js +++ b/src/app/g3w-ol/interactions/measureinteraction.js @@ -1,10 +1,9 @@ -const { t } = require('core/i18n/i18n.service'); -const { - createMeasureTooltip, - setMeasureTooltipStatic, - removeMeasureTooltip, - needUseSphereMethods -} = require('utils/ol'); +import { createMeasureTooltip } from 'utils/createMeasureTooltip'; +import { setMeasureTooltipStatic } from 'utils/setMeasureTooltipStatic'; +import { removeMeasureTooltip } from 'utils/removeMeasureTooltip'; +import { needUseSphereMethods } from 'utils/needUseSphereMethods'; + +const { t } = require('core/i18n/i18n.service'); const MeasureIteraction = function(options={}) { this._helpTooltip; diff --git a/src/app/g3w-ol/layers/rasters.js b/src/app/g3w-ol/layers/rasters.js index 65c79c35d..09954ebdc 100755 --- a/src/app/g3w-ol/layers/rasters.js +++ b/src/app/g3w-ol/layers/rasters.js @@ -1,4 +1,6 @@ -const DPI = require('utils/ol').getDPI(); +import { getDPI } from 'utils/getDPI'; + +const DPI = getDPI(); const RasterLayers = {}; diff --git a/src/app/gui/map/mapservice.js b/src/app/gui/map/mapservice.js index 645447051..5281c8a4e 100644 --- a/src/app/gui/map/mapservice.js +++ b/src/app/gui/map/mapservice.js @@ -1,12 +1,14 @@ -import { MAP_SETTINGS } from 'app/constant'; -import DataRouterService from 'services/data'; -import MapLayersStoresRegistry from 'store/map-layers'; -import ProjectsRegistry from 'store/projects'; -import ApplicationService from 'services/application'; -import ControlsRegistry from 'store/map-controls'; -import GUI from 'services/gui'; -import MapControlZoomHistory from "components/MapControlZoomHistory.vue"; -import MapControlGeocoding from 'components/MapControlGeocoding.vue'; +import { MAP_SETTINGS } from 'app/constant'; +import DataRouterService from 'services/data'; +import MapLayersStoresRegistry from 'store/map-layers'; +import ProjectsRegistry from 'store/projects'; +import ApplicationService from 'services/application'; +import ControlsRegistry from 'store/map-controls'; +import GUI from 'services/gui'; +import MapControlZoomHistory from "components/MapControlZoomHistory.vue"; +import MapControlGeocoding from 'components/MapControlGeocoding.vue'; +import { getScaleFromResolution } from 'utils/getScaleFromResolution'; +import { getResolutionFromScale } from 'utils/getResolutionFromScale'; const { inherit, @@ -26,10 +28,6 @@ const { getGeoTIFFfromServer, } = require('utils/geo'); const BaseLayers = require('g3w-ol/layers/bases'); -const { - getScaleFromResolution, - getResolutionFromScale -} = require('utils/ol'); const VectorLayer = require('core/layers/vectorlayer'); const Control = require('g3w-ol/controls/control'); diff --git a/src/app/gui/print/printservice.js b/src/app/gui/print/printservice.js index 89d95c1d8..65c2d58f3 100644 --- a/src/app/gui/print/printservice.js +++ b/src/app/gui/print/printservice.js @@ -1,12 +1,16 @@ -import ProjectsRegistry from 'store/projects'; -import ApplicationService from 'services/application'; -import ApplicationState from 'store/application-state'; +import ProjectsRegistry from 'store/projects'; +import ApplicationService from 'services/application'; +import ApplicationState from 'store/application-state'; import { PRINT_SCALES as scale, PRINT_RESOLUTIONS as dpis, PRINT_FORMATS as formats -} from 'app/constant'; -import GUI from 'services/gui'; +} from 'app/constant'; +import GUI from 'services/gui'; +import { getScaleFromResolution } from 'utils/getScaleFromResolution'; +import { getResolutionFromScale } from 'utils/getResolutionFromScale'; +import { getMetersFromDegrees } from 'utils/getMetersFromDegrees'; + const { base, @@ -16,11 +20,6 @@ const { } = require('utils'); const { t } = require('core/i18n/i18n.service'); const G3WObject = require('core/g3wobject'); -const { - getScaleFromResolution, - getResolutionFromScale, - getMetersFromDegrees -} = require('utils/ol'); const PrintPage = require('gui/print/vue/printpage'); /* diff --git a/src/utils/createMeasureTooltip.js b/src/utils/createMeasureTooltip.js new file mode 100644 index 000000000..3180886e1 --- /dev/null +++ b/src/utils/createMeasureTooltip.js @@ -0,0 +1,38 @@ +import { formatMeasure } from 'utils/formatMeasure'; + +/** + * create and add measure tooltip + */ +export function createMeasureTooltip({ map, feature } = {}, options = {}) { + const element = document.createElement('div'); + element.className = 'mtooltip mtooltip-measure'; + + const tooltip = new ol.Overlay({ + element, + offset: [0, -15], + positioning: 'bottom-center' + }); + + map.addOverlay(tooltip); + + return { + tooltip, + unbyKey: feature + .getGeometry() + .on('change', evt => { + let tooltipCoord; + const geometry = evt.target; + if (geometry instanceof ol.geom.Polygon) { + tooltipCoord = geometry.getInteriorPoint().getCoordinates(); + } else if (geometry instanceof ol.geom.MultiPolygon) { + tooltipCoord = geometry.getInteriorPoints().getCoordinates()[0]; + } else if (geometry instanceof ol.geom.LineString) { + tooltipCoord = geometry.getLastCoordinate(); + } else if (geometry instanceof ol.geom.MultiLineString) { + tooltipCoord = geometry.getLastCoordinate(); + } + element.innerHTML = formatMeasure({geometry, projection: map.getView().getProjection()}, options); + tooltip.setPosition(tooltipCoord); + }) + } +} \ No newline at end of file diff --git a/src/utils/createPolygonLayerFromBBox.js b/src/utils/createPolygonLayerFromBBox.js new file mode 100644 index 000000000..09bb034e7 --- /dev/null +++ b/src/utils/createPolygonLayerFromBBox.js @@ -0,0 +1,14 @@ +/** + * Create a polygon vector layer from bbox + * + * @param bbox + * + * @returns { ol.layer.Vector } + */ +export function createPolygonLayerFromBBox(bbox) { + return new ol.layer.Vector({ + source: new ol.source.Vector({ + features: [ new ol.Feature(new ol.geom.Polygon.fromExtent(bbox)) ] + }) + }); +} \ No newline at end of file diff --git a/src/utils/formatMeasure.js b/src/utils/formatMeasure.js new file mode 100644 index 000000000..c3f9057cb --- /dev/null +++ b/src/utils/formatMeasure.js @@ -0,0 +1,29 @@ +import { getCurrentMapUnit } from 'utils/getCurrentMapUnit'; +import { getLengthMessageText } from 'utils/getLengthMessageText'; +import { getAreaMessageText } from 'utils/getAreaMessageText'; + +export function formatMeasure({ geometry, projection } = {}, options = {}) { + /** + * @FIXME circular dependency (ie. empty object when importing at top level), ref: #130 + */ + const { Geometry, multiGeometryToSingleGeometries } = require('utils/geo'); + // + const geometryType = geometry.getType(); + const unit = getCurrentMapUnit(); + if (Geometry.isLineGeometryType(geometryType)) { + return getLengthMessageText({ + unit, + projection, + geometry + }); + } else if (Geometry.isPolygonGeometryType(geometryType)){ + let segments; + if (Geometry.isMultiGeometry(geometryType)) { + segments = []; + multiGeometryToSingleGeometries(geometry).forEach(geometry => { + geometry.getLinearRing().getCoordinates().forEach(coordinates => segments.push(coordinates)) + }) + } else segments = geometry.getLinearRing().getCoordinates(); + return getAreaMessageText({unit, geometry, projection, segments}); + } +}; \ No newline at end of file diff --git a/src/utils/getAreaMessageText.js b/src/utils/getAreaMessageText.js new file mode 100644 index 000000000..559229758 --- /dev/null +++ b/src/utils/getAreaMessageText.js @@ -0,0 +1,36 @@ +import { needUseSphereMethods } from 'utils/needUseSphereMethods'; +import { getLengthMessageText } from 'utils/getLengthMessageText'; +import { transformMeterArea } from 'utils/transformMeterArea'; + +export function getAreaMessageText({ + unit, + geometry, + projection, + segments = [] +}) { + const useSphereMethods = needUseSphereMethods(projection); + const area = Math.round(useSphereMethods ? ol.sphere.getArea(geometry, { + projection: projection.getCode() + }): geometry.getArea()); + let message; + let segments_info_meausure = ''; + const segmentLength = segments.length; + if (segmentLength > 2) { + segments_info_meausure+=`${getLengthMessageText({ + unit, + projection, + geometry: new ol.geom.LineString(segments) + })}
`; + } + switch (unit) { + case 'nautical': + message = `${transformMeterArea(area, unit)}  nmi²`; + break; + case 'metric': + default: + message = area > 1000000 ? `${(Math.round(area / 1000000 * 100) / 100).toFixed(6)} km2` : `${(Math.round(area * 100) / 100).toFixed(3)} m2`; + } + if (segments_info_meausure) + message =`Area: ${message}
${segments_info_meausure}`; + return message; +}; \ No newline at end of file diff --git a/src/utils/getCurrentMapUnit.js b/src/utils/getCurrentMapUnit.js new file mode 100644 index 000000000..6d4bb7024 --- /dev/null +++ b/src/utils/getCurrentMapUnit.js @@ -0,0 +1,8 @@ +import ApplicationState from 'store/application-state'; + +/** + * @FIXME utility functions should be stateles (move it elsewhere) + */ +export function getCurrentMapUnit() { + return ApplicationState.map.unit; +} \ No newline at end of file diff --git a/src/utils/getDPI.js b/src/utils/getDPI.js new file mode 100644 index 000000000..1e28ca084 --- /dev/null +++ b/src/utils/getDPI.js @@ -0,0 +1,8 @@ +import { DOTS_PER_INCH } from 'constant'; + +/** + * @returns { number } + */ +export function getDPI() { + return DOTS_PER_INCH; +}; \ No newline at end of file diff --git a/src/utils/getExtentForViewAndSize.js b/src/utils/getExtentForViewAndSize.js new file mode 100644 index 000000000..211eedcbf --- /dev/null +++ b/src/utils/getExtentForViewAndSize.js @@ -0,0 +1,37 @@ +/** + * @param center + * @param resolution + * @param rotation + * @param size + * + * @returns { number[] } + */ +export function getExtentForViewAndSize(center, resolution, rotation, size) { + const dx = resolution * size[0] / 2; + const dy = resolution * size[1] / 2; + const cosRotation = Math.cos(rotation); + const sinRotation = Math.sin(rotation); + const xCos = dx * cosRotation; + const xSin = dx * sinRotation; + const yCos = dy * cosRotation; + const ySin = dy * sinRotation; + const x = center[0]; + const y = center[1]; + const x0 = x - xCos + ySin; + const x1 = x - xCos - ySin; + const x2 = x + xCos - ySin; + const x3 = x + xCos + ySin; + const y0 = y - xSin - yCos; + const y1 = y - xSin + yCos; + const y2 = y + xSin + yCos; + const y3 = y + xSin - yCos; + + //return [Math.min(y0, y1, y2, y3),Math.min(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), Math.max(x0, x1, x2, x3)] + + return [ + Math.min(x0, x1, x2, x3), + Math.min(y0, y1, y2, y3), + Math.max(x0, x1, x2, x3), + Math.max(y0, y1, y2, y3) + ]; +} \ No newline at end of file diff --git a/src/utils/getLengthMessageText.js b/src/utils/getLengthMessageText.js new file mode 100644 index 000000000..53f319061 --- /dev/null +++ b/src/utils/getLengthMessageText.js @@ -0,0 +1,37 @@ +import { needUseSphereMethods } from 'utils/needUseSphereMethods'; +import { transformMeterLength } from 'utils/transformMeterLength'; + +/** + * @param { Object } opts + * @param opts.unit + * @param opts.projection + * @param opts.geometry + */ +export function getLengthMessageText({ + unit, + projection, + geometry +} = {}){ + /** + * @FIXME circular dependency (ie. empty object when importing at top level), ref: #130 + */ + const { Geometry } = require('utils/geo'); + // + const geometryType = geometry.getType(); + const useSphereMethods = needUseSphereMethods(projection); + const length = useSphereMethods ? ol.sphere.getLength(geometry, { + projection: projection.getCode() + }) : Geometry.isMultiGeometry(geometryType) ? + geometry.getLineStrings().reduce((totalLength, lineGeometry) => totalLength+= lineGeometry.getLength(), 0) + : geometry.getLength(); + let message; + switch(unit) { + case 'nautical': + message = `${transformMeterLength(length, unit)} nm`; + break; + case 'metric': + default: + message = (length > 1000) ? `${(Math.round(length / 1000 * 100) / 100).toFixed(3)} km` : `${(Math.round(length * 100) / 100).toFixed(2)} m`; + } + return message; +}; \ No newline at end of file diff --git a/src/utils/getMetersFromDegrees.js b/src/utils/getMetersFromDegrees.js new file mode 100644 index 000000000..b0d1f490a --- /dev/null +++ b/src/utils/getMetersFromDegrees.js @@ -0,0 +1,7 @@ +/** + * @param { number } degrees + * @returns { number } + */ +export function getMetersFromDegrees(degrees) { + return degrees * ol.proj.Units.METERS_PER_UNIT.degrees; +}; \ No newline at end of file diff --git a/src/utils/getResolutionFromScale.js b/src/utils/getResolutionFromScale.js new file mode 100644 index 000000000..7b1114f21 --- /dev/null +++ b/src/utils/getResolutionFromScale.js @@ -0,0 +1,6 @@ +import { INCHES_PER_UNIT, DOTS_PER_INCH } from 'constant'; + +export function getResolutionFromScale(scale, units = 'm') { + const normScale = (scale >= 1.0) ? (1.0 / scale) : scale; // just to prevent that scale is passed as 1:10000 or 0.0001 + return 1 / (normScale * INCHES_PER_UNIT[units] * DOTS_PER_INCH); +}; \ No newline at end of file diff --git a/src/utils/getScaleFromResolution.js b/src/utils/getScaleFromResolution.js new file mode 100644 index 000000000..f9fcff405 --- /dev/null +++ b/src/utils/getScaleFromResolution.js @@ -0,0 +1,5 @@ +import { INCHES_PER_UNIT, DOTS_PER_INCH } from 'constant'; + +export function getScaleFromResolution(resolution, units = 'm') { + return Math.round(resolution * INCHES_PER_UNIT[units] * DOTS_PER_INCH); +}; \ No newline at end of file diff --git a/src/utils/mergeOptions.js b/src/utils/mergeOptions.js new file mode 100644 index 000000000..760f52844 --- /dev/null +++ b/src/utils/mergeOptions.js @@ -0,0 +1,3 @@ +export function mergeOptions(obj1, obj2) { + return { ...obj1, ...obj2 }; +}; \ No newline at end of file diff --git a/src/utils/needUseSphereMethods.js b/src/utils/needUseSphereMethods.js new file mode 100644 index 000000000..630e6c50c --- /dev/null +++ b/src/utils/needUseSphereMethods.js @@ -0,0 +1,9 @@ +/** + * @param projection + * + * @returns { boolean } + */ +export function needUseSphereMethods(projection) { + return 'EPSG:3857' === projection.getCode() || 'degrees' === projection.getUnits(); + //return projection.getUnits() === 'degrees'; +}; \ No newline at end of file diff --git a/src/utils/ol/index.js b/src/utils/ol/index.js deleted file mode 100644 index 2046b25f9..000000000 --- a/src/utils/ol/index.js +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @file ORIGINAL SOURCE: src/app/core/utils/ol.js@3.8 - * - * @since 3.9.0 - */ - -/** - * @FIXME remove weird import (utility functions should be stateles) - */ -import ApplicationState from 'store/application-state'; - -/** - * @FIXME circular dependency (ie. empty object when importing at top level), ref: #130 - */ -// const { Geometry } = require('utils/geo'); - -const INCHES_PER_UNIT = { - m: 39.37, // - degrees: 4374754 -}; - -//const DOTS_PER_INCH = ol.has.DEVICE_PIXEL_RATIO * 96; //DPI96 -const DOTS_PER_INCH = 96; //DPI96 - -const utils = { - merge(obj1,obj2){ - const obj3 = { - ...obj1, - ...obj2 - }; - return obj3; - }, - - getExtentForViewAndSize(center, resolution, rotation, size) { - const dx = resolution * size[0] / 2; - const dy = resolution * size[1] / 2; - const cosRotation = Math.cos(rotation); - const sinRotation = Math.sin(rotation); - const xCos = dx * cosRotation; - const xSin = dx * sinRotation; - const yCos = dy * cosRotation; - const ySin = dy * sinRotation; - const x = center[0]; - const y = center[1]; - const x0 = x - xCos + ySin; - const x1 = x - xCos - ySin; - const x2 = x + xCos - ySin; - const x3 = x + xCos + ySin; - const y0 = y - xSin - yCos; - const y1 = y - xSin + yCos; - const y2 = y + xSin + yCos; - const y3 = y + xSin - yCos; - //return [Math.min(y0, y1, y2, y3),Math.min(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), Math.max(x0, x1, x2, x3)] - return [Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3)] - }, - // function that create a polygon vector layer from bbox - createPolygonLayerFromBBox(bbox) { - const polygonFeature = new ol.Feature(new ol.geom.Polygon.fromExtent(bbox)); - const vectorSource = new ol.source.Vector({ - features: [polygonFeature] - }); - const polygonLayer = new ol.layer.Vector({ - source: vectorSource - }); - return polygonLayer; - }, - reverseGeometry(geometry) { - const reverseCoordinates = (coordinates) => { - coordinates.find((coordinate) => { - if (Array.isArray(coordinate)) { - reverseCoordinates(coordinate) - } else { - const [y, x] = coordinates; - coordinates[0] = x; - coordinates[1] = y; - return true - } - }) - }; - let coordinates = geometry.getCoordinates(); - reverseCoordinates(coordinates); - geometry.setCoordinates(coordinates); - return geometry - }, - getScaleFromResolution(resolution, units="m") { - return Math.round(resolution * INCHES_PER_UNIT[units] * DOTS_PER_INCH); - }, - getResolutionFromScale(scale, units="m") { - const normScale = (scale >= 1.0) ? (1.0 / scale) : scale; // just to prevent that scale is passed as 1:10000 or 0.0001 - return 1 / (normScale * INCHES_PER_UNIT[units] * DOTS_PER_INCH); - }, - getDPI() { - return DOTS_PER_INCH; - }, - getMetersFromDegrees(degrees) { - return degrees * ol.proj.Units.METERS_PER_UNIT.degrees; - }, - needUseSphereMethods(projection){ - return projection.getCode() === 'EPSG:3857' || projection.getUnits() === 'degrees'; - //return projection.getUnits() === 'degrees'; - }, - getLengthMessageText({unit, projection, geometry}={}){ - /** - * @FIXME circular dependency (ie. empty object when importing at top level), ref: #130 - */ - const { Geometry } = require('utils/geo'); - // - const geometryType = geometry.getType(); - const useSphereMethods = this.needUseSphereMethods(projection); - const length = useSphereMethods ? ol.sphere.getLength(geometry, { - projection: projection.getCode() - }) : Geometry.isMultiGeometry(geometryType) ? - geometry.getLineStrings().reduce((totalLength, lineGeometry) => totalLength+= lineGeometry.getLength(), 0) - : geometry.getLength(); - let message; - switch(unit) { - case 'nautical': - message = `${this.transformMeterLength(length, unit)} nm`; - break; - case 'metric': - default: - message = (length > 1000) ? `${(Math.round(length / 1000 * 100) / 100).toFixed(3)} km` : `${(Math.round(length * 100) / 100).toFixed(2)} m`; - } - return message; - }, - getAreaMessageText({unit, geometry, projection, segments=[]}){ - const useSphereMethods = this.needUseSphereMethods(projection); - const area = Math.round(useSphereMethods ? ol.sphere.getArea(geometry, { - projection: projection.getCode() - }): geometry.getArea()); - let message; - let segments_info_meausure = ''; - const segmentLength = segments.length; - if (segmentLength > 2) { - segments_info_meausure+=`${this.getLengthMessageText({ - unit, - projection, - geometry: new ol.geom.LineString(segments) - })}
`; - } - switch (unit) { - case 'nautical': - message = `${this.transformMeterArea(area, unit)}  nmi²`; - break; - case 'metric': - default: - message = area > 1000000 ? `${(Math.round(area / 1000000 * 100) / 100).toFixed(6)} km2` : `${(Math.round(area * 100) / 100).toFixed(3)} m2`; - } - if (segments_info_meausure) - message =`Area: ${message}
${segments_info_meausure}`; - return message; - }, - formatMeasure({geometry, projection}={}, options={}){ - /** - * @FIXME circular dependency (ie. empty object when importing at top level), ref: #130 - */ - const { Geometry, multiGeometryToSingleGeometries } = require('utils/geo'); - // - const geometryType = geometry.getType(); - const unit = this.getCurrentMapUnit(); - if (Geometry.isLineGeometryType(geometryType)) { - return this.getLengthMessageText({ - unit, - projection, - geometry - }); - } else if (Geometry.isPolygonGeometryType(geometryType)){ - let segments; - if (Geometry.isMultiGeometry(geometryType)) { - segments = []; - multiGeometryToSingleGeometries(geometry).forEach(geometry => { - geometry.getLinearRing().getCoordinates().forEach(coordinates => segments.push(coordinates)) - }) - } else segments = geometry.getLinearRing().getCoordinates(); - return this.getAreaMessageText({unit, geometry, projection, segments}); - } - }, - - //create and add measure tooltip - createMeasureTooltip({map, feature}={}, options={}){ - const element = document.createElement('div'); - element.className = 'mtooltip mtooltip-measure'; - const tooltip = new ol.Overlay({ - element, - offset: [0, -15], - positioning: 'bottom-center' - }); - map.addOverlay(tooltip); - const unbyKey = feature.getGeometry().on('change', evt => { - let tooltipCoord; - const geometry = evt.target; - if (geometry instanceof ol.geom.Polygon) tooltipCoord = geometry.getInteriorPoint().getCoordinates(); - else if(geometry instanceof ol.geom.MultiPolygon) tooltipCoord = geometry.getInteriorPoints().getCoordinates()[0]; - else if (geometry instanceof ol.geom.LineString) tooltipCoord = geometry.getLastCoordinate(); - else if (geometry instanceof ol.geom.MultiLineString) tooltipCoord = geometry.getLastCoordinate(); - const output = utils.formatMeasure({ - geometry, - projection: map.getView().getProjection() - }, - options - ); - element.innerHTML = output; - tooltip.setPosition(tooltipCoord); - }); - - return { - tooltip, - unbyKey - } - }, - /** - * @FIXME utility functions should be stateles (move it elsewhere) - */ - getCurrentMapUnit(){ - return ApplicationState.map.unit; - }, - - /** - * Method to transform length meter in a specific unti (ex.nautilcal mile) - * @param length - * @param tounit - * @returns {null} - */ - transformMeterLength(length, tounit){ - switch (tounit) { - case 'nautical': - length = length * 0.0005399568; - break; - } - return length - }, - transformMeterArea(area, tounit){ - switch (tounit) { - case 'nautical': - area = area * 0.000000291553349598122862913947445759414840765222583489217190918463024037990567; - break; - } - return area; - }, - - //remove mesure tootltip - removeMeasureTooltip({map, tooltip, unbyKey}){ - map.removeOverlay(tooltip); - ol.Observable.unByKey(unbyKey); - }, - - setMeasureTooltipStatic(tooltip){ - const element = tooltip.getElement(); - element.className = 'mtooltip mtooltip-static'; - tooltip.setOffset([0, -7]); - } -}; - -module.exports = utils; diff --git a/src/utils/parsers/index.js b/src/utils/parsers/index.js index 473e2e575..37ee90f67 100644 --- a/src/utils/parsers/index.js +++ b/src/utils/parsers/index.js @@ -4,13 +4,13 @@ * @since 3.9.0 */ -import { G3W_FID } from 'app/constant'; -import GUI from 'services/gui'; +import { G3W_FID } from 'app/constant'; +import GUI from 'services/gui'; +import { reverseGeometry } from 'utils/reverseGeometry'; const { toRawType } = require('utils'); const Feature = require('core/layers/features/feature'); const { t } = require('core/i18n/i18n.service'); -const olutils = require('utils/ol'); const WORD_NUMERIC_FIELD_ESCAPE = 'GIS3W_ESCAPE_NUMERIC_FIELD_'; @@ -162,7 +162,7 @@ const utils = { reverseFeaturesCoordinates(features) { features.forEach(feature => { const geometry = feature.getGeometry(); - feature.setGeometry(olutils.reverseGeometry(geometry)) + feature.setGeometry(reverseGeometry(geometry)) }); return features }, diff --git a/src/utils/removeMeasureTooltip.js b/src/utils/removeMeasureTooltip.js new file mode 100644 index 000000000..d17bb2af2 --- /dev/null +++ b/src/utils/removeMeasureTooltip.js @@ -0,0 +1,16 @@ +/** + * Remove mesure tootltip + * + * @param { Object } opts + * @param opts.map + * @param opts.tooltip + * @param opts.unByKey + */ +export function removeMeasureTooltip({ + map, + tooltip, + unbyKey, +}) { + map.removeOverlay(tooltip); + ol.Observable.unByKey(unbyKey); +} \ No newline at end of file diff --git a/src/utils/reverseGeometry.js b/src/utils/reverseGeometry.js new file mode 100644 index 000000000..0e4e7aca6 --- /dev/null +++ b/src/utils/reverseGeometry.js @@ -0,0 +1,18 @@ +/** + * @param geometry + */ +export function reverseGeometry(geometry) { + geometry.setCoordinates(_reverseCoords(geometry.getCoordinates())); + return geometry +}; + +function _reverseCoords(c) { + c.find(c => { + if (!Array.isArray(c)) { + const [y, x] = c; c[0] = x; c[1] = y; + return true; + } + _reverseCoords(c); + }); + return c; +} \ No newline at end of file diff --git a/src/utils/setMeasureTooltipStatic.js b/src/utils/setMeasureTooltipStatic.js new file mode 100644 index 000000000..959e0e8e0 --- /dev/null +++ b/src/utils/setMeasureTooltipStatic.js @@ -0,0 +1,7 @@ +/** + * @param tooltip + */ +export function setMeasureTooltipStatic(tooltip) { + tooltip.getElement().className = 'mtooltip mtooltip-static'; + tooltip.setOffset([0, -7]); +} \ No newline at end of file diff --git a/src/utils/transformMeterArea.js b/src/utils/transformMeterArea.js new file mode 100644 index 000000000..d201bf2ab --- /dev/null +++ b/src/utils/transformMeterArea.js @@ -0,0 +1,12 @@ +/** + * @param area + * @param tounit + * + * @returns { number } + */ +export function transformMeterArea(area, tounit) { + if ('nautical' === tounit) { + return area * 0.000000291553349598122862913947445759414840765222583489217190918463024037990567; + } + return area; +}; \ No newline at end of file diff --git a/src/utils/transformMeterLength.js b/src/utils/transformMeterLength.js new file mode 100644 index 000000000..8bc33d77b --- /dev/null +++ b/src/utils/transformMeterLength.js @@ -0,0 +1,14 @@ +/** + * Transform length meter in a specific unit (ex.nautical mile) + * + * @param length + * @param tounit + * + * @returns { number } + */ +export function transformMeterLength(length, tounit) { + if ('nautical' === tounit) { + return length * 0.0005399568; + } + return length; +} \ No newline at end of file