From c6758273e659fb382fc824cebd6860df3f7f3c10 Mon Sep 17 00:00:00 2001 From: Paschalis Mpeis Date: Mon, 18 Oct 2021 19:20:02 +0300 Subject: [PATCH 01/16] Fixed rotation bug while zooming to align a floorplan --- .../libs/jquery.ui.rotatable.js | 362 ++++++++++++++++++ .../libs/jquery.ui.rotatable.min.js | 8 - .../anyplace_viewer/style/floorplan.css | 11 - .../style/floorplan.css | 11 - .../style => shared/css}/floorplan.css | 3 +- .../public/shared/images/icon-rotate-orig.png | Bin 0 -> 7644 bytes server/public/shared/images/icon-rotate.svg | 20 + server/public/shared/images/vessel-icon.png | Bin 0 -> 33805 bytes server/public/shared/images/vessel-icon.svg | 131 +++++++ 9 files changed, 515 insertions(+), 31 deletions(-) create mode 100644 server/public/anyplace_architect/libs/jquery.ui.rotatable.js delete mode 100644 server/public/anyplace_architect/libs/jquery.ui.rotatable.min.js delete mode 100644 server/public/anyplace_viewer/style/floorplan.css delete mode 100644 server/public/anyplace_viewer_campus/style/floorplan.css rename server/public/{anyplace_architect/style => shared/css}/floorplan.css (77%) create mode 100644 server/public/shared/images/icon-rotate-orig.png create mode 100644 server/public/shared/images/icon-rotate.svg create mode 100644 server/public/shared/images/vessel-icon.png create mode 100644 server/public/shared/images/vessel-icon.svg diff --git a/server/public/anyplace_architect/libs/jquery.ui.rotatable.js b/server/public/anyplace_architect/libs/jquery.ui.rotatable.js new file mode 100644 index 000000000..e0acc6d7e --- /dev/null +++ b/server/public/anyplace_architect/libs/jquery.ui.rotatable.js @@ -0,0 +1,362 @@ +/* globals define jQuery */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory) + } else { + // Browser globals + factory(jQuery) + } +}(function ($) { + $.widget('ui.rotatable', $.ui.mouse, { + widgetEventPrefix: 'rotate', + + options: { + angle: false, // specify an angle in radians (for backward compatability) + degrees: false, // specify angle in degrees + handle: false, // an image to use for a handle + handleOffset: { // where the handle should appear + top: 0, + left: 0 + }, + radians: false, // specify angle in radians + rotate: null, // a callback for during rotation + rotationCenterOffset: { // offset the center of the element for rotation + top: 0, + left: 0 + }, + snap: false, // boolean flag, should the element snap to a certain rotation? + start: null, // callback when rotation starts + step: 22.5, // angle in degrees that the rotation should be snapped to + stop: null, // callback when rotation stops + transforms: null, // other transforms to performed on the element + wheelRotate: true // boolean flag, should the element rotate when the mousewheel is rotated? + }, + + // accessor for the angle in radians + angle: function (angle) { + if (angle === undefined) { + return this.options.angle + } + this.options.angle = angle + this.elementCurrentAngle = angle + this._performRotation(this.options.angle) + }, + + // calculates the element center if needed and returns it + getElementCenter: function () { + this.elementCenter = this._calculateElementCenter() + return this.elementCenter + }, + + // accessor for the handle + handle: function (handle) { + if (handle === undefined) { + return this.options.handle + } + this.options.handle = handle + }, + + plugins: {}, + + /* accessor for the center of rotation + * takes an object with keys of top and left + */ + rotationCenterOffset: function (offset) { + if (offset === undefined) { + return this.options.rotationCenterOffset + } + if (offset.top !== null) { + this.options.rotationCenterOffset.top = offset.top + } + if (offset.left !== null) { + this.options.rotationCenterOffset.left = offset.left + } + }, + + // listener for rotating the element + rotateElement: function (event) { + if (!this.element || this.element.disabled || this.options.disabled) { + return false + } + + if (!event.which) { + this.stopRotate(event) + return false + } + + var rotateAngle = this._calculateRotateAngle(event) + var previousRotateAngle = this.elementCurrentAngle + this.elementCurrentAngle = rotateAngle + + // Plugins callbacks need to be called first. + this._propagate('rotate', event) + + if (this._propagate('rotate', event) === false) { + this.elementCurrentAngle = previousRotateAngle + return false + } + var ui = this.ui() + if (this._trigger('rotate', event, ui) === false) { + this.elementCurrentAngle = previousRotateAngle + return false + } else if (ui.angle.current !== rotateAngle) { + rotateAngle = ui.angle.current + this.elementCurrentAngle = rotateAngle + } + + this._performRotation(rotateAngle) + + if (previousRotateAngle !== rotateAngle) { + this.hasRotated = true + } + + return false + }, + + // listener for starting rotation + startRotate: function (event) { + var center = this.getElementCenter() + var startXFromCenter = event.pageX - center.x + var startYFromCenter = event.pageY - center.y + this.mouseStartAngle = Math.atan2(startYFromCenter, startXFromCenter) + this.elementStartAngle = this.elementCurrentAngle + this.hasRotated = false + + this._propagate('start', event) + + $(document).bind('mousemove', this.listeners.rotateElement) + $(document).bind('mouseup', this.listeners.stopRotate) + + return false + }, + + // listener for stopping rotation + stopRotate: function (event) { + if (!this.element || this.element.disabled) { + return + } + + $(document).unbind('mousemove', this.listeners.rotateElement) + $(document).unbind('mouseup', this.listeners.stopRotate) + + this.elementStopAngle = this.elementCurrentAngle + + this._propagate('stop', event) + + setTimeout(function () { this.element = false }, 10) + return false + }, + + // listener for mousewheel rotation + wheelRotate: function (event) { + if (!this.element || this.element.disabled || this.options.disabled) { + return + } + event.preventDefault() + var angle = this._angleInRadians(Math.round(event.originalEvent.deltaY / 10)) + if (this.options.snap || event.shiftKey) { + angle = this._calculateSnap(angle) + } + angle = this.elementCurrentAngle + angle + this.angle(angle) + this._trigger('rotate', event, this.ui()) + }, + + // for callbacks + ui: function () { + return { + api: this, + element: this.element, + angle: { + start: this.elementStartAngle, + current: this.elementCurrentAngle, + degrees: this._normalizeDegrees(this._angleInDegrees(this.elementCurrentAngle)), + stop: this.elementStopAngle + } + } + }, + + /* *********************** private functions ************************** */ + // calculates the radians for a given angle in degrees + _angleInRadians: function (degrees) { + return degrees * Math.PI / 180 + }, + + // calculates the degrees for a given angle in radians + _angleInDegrees: function (radians) { + return radians * 180 / Math.PI + }, + + _normalizeDegrees: function (degrees) { + return ((degrees % 360) + 360) % 360; + }, + + // calculates the center of the element + _calculateElementCenter: function () { + var elementOffset = this._getElementOffset() + + // Rotation center given via options + if (this._isRotationCenterSet()) { + return { + x: elementOffset.left + this.rotationCenterOffset().left, + y: elementOffset.top + this.rotationCenterOffset().top + } + } + + // Deduce rotation center from transform-origin + if (this.element.css('transform-origin') !== undefined) { + var originPx = this.element.css('transform-origin').match(/([\d.]+)px +([\d.]+)px/) + if (originPx != null) { + return { + x: elementOffset.left + parseFloat(originPx[1]), + y: elementOffset.top + parseFloat(originPx[2]) + } + } + } + + // Default rotation center: middle of the element + return { + x: elementOffset.left + this.element.width() / 2, + y: elementOffset.top + this.element.height() / 2 + } + }, + + // calculates the angle that the element should snap to and returns it in radians + _calculateSnap: function (radians) { + var degrees = this._angleInDegrees(radians) + degrees = Math.round(degrees / this.options.step) * this.options.step + return this._angleInRadians(degrees) + }, + + // calculates the angle to rotate the element to, based on input + _calculateRotateAngle: function (event) { + var center = this.getElementCenter() + + var xFromCenter = event.pageX - center.x + var yFromCenter = event.pageY - center.y + var mouseAngle = Math.atan2(yFromCenter, xFromCenter) + var rotateAngle = mouseAngle - this.mouseStartAngle + this.elementStartAngle + + if (this.options.snap || event.shiftKey) { + rotateAngle = this._calculateSnap(rotateAngle) + } + + return rotateAngle + }, + + // constructor + _create: function () { + var handle + if (!this.options.handle) { + handle = $(document.createElement('div')) + handle.addClass('ui-rotatable-handle') + if (this.options.handleOffset.top !== 0 || this.options.handleOffset.left !== 0) { + handle.css('position', 'relative') + handle.css('top', this.options.handleOffset.top + 'px') + handle.css('left', this.options.handleOffset.left + 'px') + } + } else { + handle = this.options.handle + } + + this.listeners = { + rotateElement: $.proxy(this.rotateElement, this), + startRotate: $.proxy(this.startRotate, this), + stopRotate: $.proxy(this.stopRotate, this), + wheelRotate: $.proxy(this.wheelRotate, this) + } + + if (this.options.wheelRotate) { + this.element.bind('wheel', this.listeners.wheelRotate) + } + + handle.draggable({ helper: 'clone', start: this._dragStart, handle: handle }) + handle.bind('mousedown', this.listeners.startRotate) + + if (!handle.closest(this.element).length) { + handle.appendTo(this.element) + } + this.rotationCenterOffset(this.options.rotationCenterOffset) + + if (this.options.degrees) { + this.elementCurrentAngle = this._angleInRadians(this.options.degrees) + } + else { + this.elementCurrentAngle = this.options.radians || this.options.angle || 0 + } + this._performRotation(this.elementCurrentAngle) + }, + + // destructor + _destroy: function () { + this.element.removeClass('ui-rotatable') + this.element.find('.ui-rotatable-handle').remove() + + if (this.options.wheelRotate) { + this.element.unbind('wheel', this.listeners.wheelRotate) + } + }, + + // used for the handle + _dragStart: function (event) { + if (this.element) { + return false + } + }, + + // retrieves the element offset + _getElementOffset: function () { + this._performRotation(0) + var offset = this.element.offset() + this._performRotation(this.elementCurrentAngle) + return offset + }, + + _getTransforms: function (angle) { + var transforms = 'rotate(' + angle + 'rad)' + + if (this.options.transforms) { + transforms += ' ' + (function (transforms) { + var t = [] + for (var i in transforms) { + if (transforms.hasOwnProperty(i) && transforms[i]) { + t.push(i + '(' + transforms[i] + ')') + } + } + return t.join(' ') + }(this.options.transforms)) + } + return transforms + }, + + // checks to see if the element has a rotationCenterOffset set + _isRotationCenterSet: function () { + return (this.options.rotationCenterOffset.top !== 0 || this.options.rotationCenterOffset.left !== 0) + }, + + // performs the actual rotation on the element + _performRotation: function (angle) { + if (this._isRotationCenterSet()) { + this.element.css('transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset.top + 'px') + this.element.css('-ms-transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset.top + 'px') /* IE 9 */ + this.element.css('-webkit-transform-origin', this.options.rotationCenterOffset.left + 'px ' + this.options.rotationCenterOffset + 'px') /* Chrome, Safari, Opera */ + } + + var transforms = this._getTransforms(angle) + + this.element.css('transform', transforms) + this.element.css('-moz-transform', transforms) + this.element.css('-webkit-transform', transforms) + this.element.css('-o-transform', transforms) + }, + + // propagates events + _propagate: function (n, event) { + $.ui.plugin.call(this, n, [event, this.ui()]); + (n !== 'rotate' && this._trigger(n, event, this.ui())) + } + }) + + return $.ui.rotatable +})) \ No newline at end of file diff --git a/server/public/anyplace_architect/libs/jquery.ui.rotatable.min.js b/server/public/anyplace_architect/libs/jquery.ui.rotatable.min.js deleted file mode 100644 index 7847adb35..000000000 --- a/server/public/anyplace_architect/libs/jquery.ui.rotatable.min.js +++ /dev/null @@ -1,8 +0,0 @@ -(function(c,d){c.widget("ui.rotatable",c.ui.mouse,{options:{handle:!1,angle:!1,rotationCenterX:!1,rotationCenterY:!1,start:null,rotate:null,stop:null},rotationCenterX:function(a){if(a===d)return this.options.rotationCenterX;this.options.rotationCenterX=a},rotationCenterY:function(a){if(a===d)return this.options.rotationCenterY;this.options.rotationCenterY=a},handle:function(a){if(a===d)return this.options.handle;this.options.handle=a},angle:function(a){if(a===d)return this.options.angle;this.elementCurrentAngle= - this.options.angle=a;this.performRotation(this.options.angle)},_create:function(){var a;this.options.handle?a=this.options.handle:(a=c(document.createElement("div")),a.addClass("ui-rotatable-handle"));this.listeners={rotateElement:c.proxy(this.rotateElement,this),startRotate:c.proxy(this.startRotate,this),stopRotate:c.proxy(this.stopRotate,this),wheelRotate:c.proxy(this.wheelRotate,this)};this.element.bind("wheel",this.listeners.wheelRotate);a.draggable({helper:"clone",start:this.dragStart,handle:a}); - a.bind("mousedown",this.listeners.startRotate);a.appendTo(this.element);0!=this.options.angle?(this.elementCurrentAngle=this.options.angle,this.performRotation(this.elementCurrentAngle)):this.elementCurrentAngle=0},_destroy:function(){this.element.removeClass("ui-rotatable");this.element.find(".ui-rotatable-handle").remove();this.element.unbind("wheel",this.listeners.wheelRotate)},performRotation:function(a){this.element.css("transform-origin",this.options.rotationCenterX+"% "+this.options.rotationCenterY+ - "%");this.element.css("-ms-transform-origin",this.options.rotationCenterX+"% "+this.options.rotationCenterY+"%");this.element.css("-webkit-transform-origin",this.options.rotationCenterX+"% "+this.options.rotationCenterY+"%");this.element.css("transform","rotate("+a+"rad)");this.element.css("-moz-transform","rotate("+a+"rad)");this.element.css("-webkit-transform","rotate("+a+"rad)");this.element.css("-o-transform","rotate("+a+"rad)")},getElementOffset:function(){this.performRotation(0);var a=this.element.offset(); - this.performRotation(this.elementCurrentAngle);return a},getElementCenter:function(){var a=this.getElementOffset();if(!1===this.options.rotationCenterX)var b=a.left+this.element.width()/2,a=a.top+this.element.height()/2;else b=a.left+this.element.width()/100*this.options.rotationCenterX,a=a.top+this.element.height()/100*this.options.rotationCenterY;return[b,a]},dragStart:function(a){if(this.element)return!1},startRotate:function(a){var b=this.getElementCenter();this.mouseStartAngle=Math.atan2(a.pageY- - b[1],a.pageX-b[0]);this.elementStartAngle=this.elementCurrentAngle;this.hasRotated=!1;this._propagate("start",a);c(document).bind("mousemove",this.listeners.rotateElement);c(document).bind("mouseup",this.listeners.stopRotate);return!1},rotateElement:function(a){if(!this.element||this.element.disabled)return!1;var b=this.getElementCenter(),b=Math.atan2(a.pageY-b[1],a.pageX-b[0])-this.mouseStartAngle+this.elementStartAngle;if(a.shiftKey){var c=15/180*Math.PI;0>b&&(c*=-1);b-=(b+c/2)%c-c/2}this.performRotation(b); - c=this.elementCurrentAngle;this.elementCurrentAngle=b;this._propagate("rotate",a);c!=b&&(this._trigger("rotate",a,this.ui()),this.hasRotated=!0);return!1},stopRotate:function(a){if(this.element&&!this.element.disabled)return c(document).unbind("mousemove",this.listeners.rotateElement),c(document).unbind("mouseup",this.listeners.stopRotate),this.elementStopAngle=this.elementCurrentAngle,this.hasRotated&&this._propagate("stop",a),setTimeout(function(){this.element=!1},10),!1},wheelRotate:function(a){var b= - Math.round(a.originalEvent.deltaY/10)*Math.PI/180,b=this.elementCurrentAngle+b;this.angle(b);this._trigger("rotate",a,this.ui())},_propagate:function(a,b){c.ui.plugin.call(this,a,[b,this.ui()]);"rotate"!==a&&this._trigger(a,b,this.ui())},plugins:{},ui:function(){return{api:this,element:this.element,angle:{start:this.elementStartAngle,current:this.elementCurrentAngle,stop:this.elementStopAngle}}}})})(jQuery); \ No newline at end of file diff --git a/server/public/anyplace_viewer/style/floorplan.css b/server/public/anyplace_viewer/style/floorplan.css deleted file mode 100644 index 3baac5d5c..000000000 --- a/server/public/anyplace_viewer/style/floorplan.css +++ /dev/null @@ -1,11 +0,0 @@ -.ui-resizable-se, .ui-resizable-sw, .ui-resizable-nw, .ui-resizable-ne { - background-color: red; -} - -.ui-resizable-se { - cursor: se-resize; - width: 9px; - height: 9px; - right: -5px; - bottom: -5px; -} \ No newline at end of file diff --git a/server/public/anyplace_viewer_campus/style/floorplan.css b/server/public/anyplace_viewer_campus/style/floorplan.css deleted file mode 100644 index 3baac5d5c..000000000 --- a/server/public/anyplace_viewer_campus/style/floorplan.css +++ /dev/null @@ -1,11 +0,0 @@ -.ui-resizable-se, .ui-resizable-sw, .ui-resizable-nw, .ui-resizable-ne { - background-color: red; -} - -.ui-resizable-se { - cursor: se-resize; - width: 9px; - height: 9px; - right: -5px; - bottom: -5px; -} \ No newline at end of file diff --git a/server/public/anyplace_architect/style/floorplan.css b/server/public/shared/css/floorplan.css similarity index 77% rename from server/public/anyplace_architect/style/floorplan.css rename to server/public/shared/css/floorplan.css index 3baac5d5c..dfad3ef2f 100644 --- a/server/public/anyplace_architect/style/floorplan.css +++ b/server/public/shared/css/floorplan.css @@ -1,5 +1,6 @@ .ui-resizable-se, .ui-resizable-sw, .ui-resizable-nw, .ui-resizable-ne { - background-color: red; + background-color: darkred; + border-radius: 2px; } .ui-resizable-se { diff --git a/server/public/shared/images/icon-rotate-orig.png b/server/public/shared/images/icon-rotate-orig.png new file mode 100644 index 0000000000000000000000000000000000000000..1bed11ce344fe0b63efb005ec7376720636208b8 GIT binary patch literal 7644 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 + + + + + + + + diff --git a/server/public/shared/images/vessel-icon.png b/server/public/shared/images/vessel-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb5d2e05cfb653dc69a46801d763bae48b4336b0 GIT binary patch literal 33805 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 literal 0 HcmV?d00001 diff --git a/server/public/shared/images/vessel-icon.svg b/server/public/shared/images/vessel-icon.svg new file mode 100644 index 000000000..7ca8429f9 --- /dev/null +++ b/server/public/shared/images/vessel-icon.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From bed771ad4efae24bb174334863b9c5b73054b9ae Mon Sep 17 00:00:00 2001 From: Paschalis Mpeis Date: Thu, 21 Oct 2021 16:43:48 +0300 Subject: [PATCH 02/16] updating shared js --- server/public/anyplace_architect/app.js | 10 + server/public/anyplace_architect/index.html | 23 +- .../scripts/CanvasOverlay.js | 271 ++++++++++++------ server/public/anyplace_viewer/app.js | 6 +- .../controllers/FloorController.js | 4 +- server/public/anyplace_viewer_campus/app.js | 30 +- .../public/shared/js/anyplace-core-js/api.js | 5 +- .../shared/js/anyplace-core-js/shared.js | 157 ++++++++-- 8 files changed, 358 insertions(+), 148 deletions(-) diff --git a/server/public/anyplace_architect/app.js b/server/public/anyplace_architect/app.js index a43036775..85e3979f5 100644 --- a/server/public/anyplace_architect/app.js +++ b/server/public/anyplace_architect/app.js @@ -25,6 +25,9 @@ THE SOFTWARE. */ + +// TODO:PV app.js must be unified in the lib (one for architect, viewer, etc..) + var app = angular.module('anyArchitect', ['ngCookies', 'angularjs-dropdown-multiselect', 'ui.bootstrap', 'ui.select', 'ngSanitize']); app.service('GMapService', function () { @@ -233,6 +236,7 @@ app.service('GMapService', function () { app.factory('AnyplaceService', function () { var anyService = {}; + anyService.prevBuilding = undefined; anyService.selectedBuilding = undefined; anyService.selectedFloor = undefined; anyService.selectedPoi = undefined; @@ -286,6 +290,8 @@ app.factory('AnyplaceService', function () { return this.selectedFloor; }; + anyService.hasSelectedFloor = function () { return this.selectedFloor !== undefined; }; + anyService.getFloorNumber = function () { if (!this.selectedFloor) { return 'N/A'; @@ -297,7 +303,10 @@ app.factory('AnyplaceService', function () { return this.selectedFloor.floor_name; }; + // TODO:PV make this a stack. and pop on ($scope.deleteBuilding). and always select keep the top. + // if empty (after pop), then select using the normal way anyService.setBuilding = function (b) { + this.prevBuilding = this.selectedBuilding; this.selectedBuilding = b; }; @@ -370,6 +379,7 @@ app.factory('AnyplaceService', function () { anyService.clearAllData = function () { anyService.selectedPoi = undefined; anyService.selectedFloor = undefined; + anyService.prevBuilding = undefined; anyService.selectedBuilding = undefined; anyService.selectedCampus = undefined; anyService.ShowShareProp = undefined; diff --git a/server/public/anyplace_architect/index.html b/server/public/anyplace_architect/index.html index a58c705e0..25f7b2c2a 100644 --- a/server/public/anyplace_architect/index.html +++ b/server/public/anyplace_architect/index.html @@ -17,7 +17,8 @@ - + + - + - +
@@ -1596,5 +1572,36 @@ }); + + diff --git a/server/public/shared/js/anyplace-core-js/api.js b/server/public/shared/js/anyplace-core-js/api.js index d1e31ff4f..c87c0346f 100644 --- a/server/public/shared/js/anyplace-core-js/api.js +++ b/server/public/shared/js/anyplace-core-js/api.js @@ -68,8 +68,8 @@ API.Mapping.RADIO_HEATMAP_BY_TIME_TILES_URL = API.url + API.Mapping.RADIO_HEATMA API.Mapping.RADIOMAP_DELETE = "/api/auth/radiomap/delete" API.Mapping.RADIOMAP_DELETE_URL = API.url + API.Mapping.RADIOMAP_DELETE; -API.Mapping.RADIOMAP_FLOOR_ALL = "/radiomap/floor/all"; -API.Mapping.RADIOMAP_FLOOR_ALL_URL = API.url + API.Mapping.RADIOMAP_FLOOR_ALL; +API.Mapping.RADIOMAP_FLOORS = "/radiomap/floors"; +API.Mapping.RADIOMAP_FLOORS_URL = API.url + API.Mapping.RADIOMAP_FLOORS; API.Mapping.SPACE_ADD = "/auth/mapping/space/add"; API.Mapping.SPACE_ADD_URL = API.url + API.Mapping.SPACE_ADD; @@ -378,9 +378,10 @@ app.factory('AnyplaceAPIService', ['$http', '$q', 'formDataObject', function ($h }; apiService.getRadioByBuildingFloorAll = function (json_req) { + LOG.D3("getRadioByBuildingFloorAll") return $http({ method: "POST", - url: API.Mapping.RADIOMAP_FLOOR_ALL_URL, + url: API.Mapping.RADIOMAP_FLOORS_URL, data: json_req }).success(function (data, status) { return data; @@ -389,17 +390,18 @@ app.factory('AnyplaceAPIService', ['$http', '$q', 'formDataObject', function ($h }); }; - apiService.getRadioByBuildingFloorTxt = function (json_req) { - return $http({ - method: "POST", - url: API.Mapping.RADIOMAP_FLOOR_ALL_TXT_URL, - data: json_req - }).success(function (data, status) { - return data; - }).error(function (data, status) { - return data; - }); - }; + // UNUSED + // apiService.getRadioByBuildingFloorTxt = function (json_req) { + // return $http({ + // method: "POST", + // url: API.Mapping.RADIOMAP_FLOOR_ALL_TXT_URL, + // data: json_req + // }).success(function (data, status) { + // return data; + // }).error(function (data, status) { + // return data; + // }); + // }; /************************************************** * BUILDING FUNCTIONS @@ -647,10 +649,12 @@ app.factory('AnyplaceAPIService', ['$http', '$q', 'formDataObject', function ($h }); }; - apiService.downloadFloorPlanAll = function (json_req, buid, floor_number) { + apiService.downloadFloorPlanAll = function (json_req, buid, floors) { + LOG.D("downloadFloorPlanAll") + LOG.D(floors) return $http({ method: "POST", - url: API.Mapping.FLOOR_PLAN_DOWNLOAD_URL_ALL + buid + "/" + floor_number, + url: API.Mapping.FLOOR_PLAN_DOWNLOAD_URL_ALL + buid + "/" + floors, data: json_req }).success(function (data, status) { return data; diff --git a/server/public/shared/js/anyplace-core-js/const.js b/server/public/shared/js/anyplace-core-js/const.js index f7d0f1679..b58ea41df 100644 --- a/server/public/shared/js/anyplace-core-js/const.js +++ b/server/public/shared/js/anyplace-core-js/const.js @@ -6,15 +6,17 @@ var DEFAULT_MAP_TILES = "CartoLight"; // MESSAGES //// Error messages -ERR_FETCH_BUILDINGS="Something went wrong while fetching buildings."; -ERR_FETCH_ALL_FLOORS="Something went wrong while fetching all floors."; -ERR_USER_AUTH="Could not authorize user. Please refresh."; -ERR_FETCH_FINGERPRINTS="Something went wrong while fetching fingerprints."; -ERR_GEOLOC_DEVICE_SETTINGS="Please enable location access."; +ERR_FETCH_BUILDINGS="Error while fetching buildings"; +ERR_FETCH_ALL_FLOORS="Error while fetching floors"; +ERR_FETCH_ALL_FLOORPLANS="Error while fetching floorplans"; +ERR_FETCH_ALL_RADIOMAPS="Error while fetching radiomaps"; +ERR_USER_AUTH="Could not authorize user. Please refresh"; +ERR_FETCH_FINGERPRINTS="Error while fetching fingerprints"; +ERR_GEOLOC_DEVICE_SETTINGS="Please enable location access"; ERR_GEOLOC_NET_OR_SATELLITES="Position unavailable. The network is down or the positioning satellites couldn't be contacted."; -ERR_GEOLOC_TIMEOUT="Timeout. The request for retrieving your Geolocation was timed out."; -ERR_GEOLOC_UNKNOWN="There was an error while retrieving your Geolocation. Please try again."; -ERR_GEOLOC_NOT_SUPPORTED="The Geolocation feature is not supported by this browser."; +ERR_GEOLOC_TIMEOUT="Timeout on the geolocation request."; +ERR_GEOLOC_UNKNOWN="Error while geolocating. Please try again."; +ERR_GEOLOC_NOT_SUPPORTED="Geolocation not supported by the browser."; WARN_NO_FINGERPRINTS="This floor seems not to be FingerPrint mapped. Download the Anyplace app from the Google Play store to map the floor."; WARN_ACCES_REMOVED="ACCES map removed."; \ No newline at end of file From 3bcc9a5fb2d17e283b578dce831e0a91a7787e68 Mon Sep 17 00:00:00 2001 From: Paschalis Mpeis Date: Mon, 25 Oct 2021 11:26:47 +0300 Subject: [PATCH 15/16] pumping backend version, changelog + logged minor api chaneg --- server/API_CHANGES.md | 2 +- server/CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ server/conf/app.base.conf | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/server/API_CHANGES.md b/server/API_CHANGES.md index 52d133b9e..0f14514de 100644 --- a/server/API_CHANGES.md +++ b/server/API_CHANGES.md @@ -44,7 +44,7 @@ | /anyplace/position/predictFloorAlgo1 | /api/position/predictFloorAlgo1 | | | /anyplace/position/estimate_position | /api/position/estimate | | | /anyplace/position/radio/delete | /api/radiomap/delete | | -| /anyplace/position/radio_by_building_floor_all | /api/radiomap/floor/all | | +| /anyplace/position/radio_by_building_floor_all | /api/radiomap/floors | | | /anyplace/position/radio_by_floor_bbox | /api/radiomap/floor/bbox | | | /anyplace/position/radio_upload | /api/radiomap/upload | | | /anyplace/position/radio_download_floor | /api/radiomap/floor | | diff --git a/server/CHANGELOG.md b/server/CHANGELOG.md index 650b61e6a..89e82813e 100644 --- a/server/CHANGELOG.md +++ b/server/CHANGELOG.md @@ -1,4 +1,34 @@ # CHANGELOG + +# Version 4.2.6 +- search bar (pac-input): + - accepts coordinates in google maps format: LAT, LON + - moves map to that location + - previous functionality is not working due to GMap.js update +- uploading floorplans + - `OverlayMode`: + - keep the previous floor's floorplan when uploading a new one to aid in aligning + - no longer rotating when zooming in. the `rotatable` plugin was also updated + - while zooming, rotating and panning a floorplan: + - most of the gmaps functionality is disabled (e.g, rotation, zoom, etc) + - using the proper callbacks now (from rotatable, resizable, etc) + - not required to zoom at full level to improve quality + - image is uploaded at max quality + - canvas is re-drawn before saving at full resolution + +- share urls (and everything based on BASE_URL) is now relative + - e.g. sharing a building on ap-dev will generate an endpoint for ap-dev + +- BUGFIX: MDB issue when storing new documents + - affected vessel/building, floor creation, etc +- BUGFIX: architect no longer loads 3x on `angular init` +- BUGFIX: better handling of the cases where architect requests fingerprints/heatmaps/APs of unmapped floors. +- BUGFIX: backup now works: + - related bugs: (endpoints updated) + - `/api/floorplans64/all/{buid}/{floors}` + - `/api/radiomap/floors` +- BUGFIX: POI Connection delete + #### Archive: [changelog](changelog/README.md) --- diff --git a/server/conf/app.base.conf b/server/conf/app.base.conf index 575f723a5..247e089cd 100644 --- a/server/conf/app.base.conf +++ b/server/conf/app.base.conf @@ -3,7 +3,7 @@ ### application.name="anyplace" -application.version="4.2.5" +application.version="4.2.6" # modify the amount of information that is printed. See utils.LOG.scala application.debug.level=2 From 3df45dd89f7404fdf83cc0a0c671a730cd5f733f Mon Sep 17 00:00:00 2001 From: Paschalis Mpeis Date: Mon, 25 Oct 2021 11:38:54 +0300 Subject: [PATCH 16/16] minor bugfix --- server/public/shared/js/anyplace-core-js/shared.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/public/shared/js/anyplace-core-js/shared.js b/server/public/shared/js/anyplace-core-js/shared.js index eb8f69606..e804ef3f0 100644 --- a/server/public/shared/js/anyplace-core-js/shared.js +++ b/server/public/shared/js/anyplace-core-js/shared.js @@ -295,11 +295,12 @@ var regex_ck_lon = /^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,18})?|180(?:\.0{1,18})?)$/; /** * accepts coordinates in google maps format: * @param str e.g., '57.693431580156464, 11.913933480401484' + * * @returns {{}|null} */ function get_coordinates(str){ LOG.D4("get_coordinates: " + str) - let spaces = str.split(" "); + var spaces = str.split(" "); if (spaces.length === 2) { var lat = spaces[0].slice(0, -1); // remove last comma var lon = spaces[1]
+
@@ -732,10 +706,11 @@
NOTES: -
• Aspect ratio is forcefully respected.
-
• Image quality no longer depends on zoom level.
-
• Image quality improves after floorplan submission.
-
• Use max zoom level to improve placement accuracy.
+ + + +
• Use high zoom level to improve placement accuracy.
+
• Zoom level affects real-life size of the floorplan.
@@ -1234,6 +1209,7 @@

Wi-Fi: