Repository: openlayers/openlayers (JavaScript)

Issue:
applyTransform for empty extent will get NaN
**Describe the bug**
applyTransform for empty extent will get a extent with NaN, use this extent as param to fit view will throw error.

**To Reproduce**
simple code is below
```javascript
import { applyTransform, createEmpty } from 'ol/extent';
import { getTransform } from 'ol/proj';

function fit(geomOrExtent) {
  // default value
  let extent = createEmpty();
  if (isArray(geomOrExtent)) {
    // assignment by case
  }

  extent = applyTransform(extent, getTransform('EPSG:4326', 'EPSG:3857'));
  map.getView().fit(extent);
}
```

then get error
<img width="613" alt="image" src="https://user-images.githubusercontent.com/22017489/228728865-77f2f818-e6b8-4965-aac7-42d39694ef0c.png">


i found it actually cause by `fromEPSG4326` which return by `getTransform('EPSG:4326', 'EPSG:3857')`
more precisely, it's caused by `Math.tan` will transform `Infinity` to `NaN`.
see below
<img width="849" alt="image" src="https://user-images.githubusercontent.com/22017489/228727872-7b7dac45-0b75-4027-b0d0-cd9634426f47.png">

**Expected behavior**
`fromEPSG4326` can handle empty extent (`Infinity`) correctly


btw, the background is that my user use EPSG:4326, but tile service use EPSG:3857
so I need to do a conversion at the input and output.



Discussion:
I agree a empty extent should be empty in any projection.  However, it is likely any application would need to test `isEmpty` at some point in code such as yours.  Even if `applyTransform` made a special case (similar to `getArea`) for an empty extent you would still get a `Cannot fit empty extent provided as 'geometry'` assertion at the next line.  
Also if an extent does not contain `Infinity` but is considered empty because it has negative width or height, a transform will not be empty as it will use the bounding extent of the corners regardless of their original order.

That also happens if an identity transform is used on an empty extent:

```
let extent = createEmpty();
extent = transformExtent(extent, 'EPSG:4326', 'EPSG:4326');
console.log(isEmpty(extent), extent);  // false, [-Infinity, -Infinity, Infinity, Infinity]
```



Patch:
diff --git a/src/ol/extent.js b/src/ol/extent.js
index 833c0bbd319..eed8667a878 100644
--- a/src/ol/extent.js
+++ b/src/ol/extent.js
@@ -809,6 +809,9 @@ export function intersectsSegment(extent, start, end) {
  * @api
  */
 export function applyTransform(extent, transformFn, dest, stops) {
+  if (isEmpty(extent)) {
+    return createOrUpdateEmpty(dest);
+  }
   let coordinates = [];
   if (stops > 1) {
     const width = extent[2] - extent[0];


Test patch:
diff --git a/test/node/ol/extent.test.js b/test/node/ol/extent.test.js
index 6313c2c0581..b9812ef1ff6 100644
--- a/test/node/ol/extent.test.js
+++ b/test/node/ol/extent.test.js
@@ -781,6 +781,27 @@ describe('ol/extent.js', function () {
   });
 
   describe('#applyTransform()', function () {
+    it('returns empty for empty extents', function () {
+      const transformFn = getTransform('EPSG:4326', 'EPSG:3857');
+      const emptyExtent = _ol_extent_.createEmpty();
+      const destEmpty = _ol_extent_.applyTransform(emptyExtent, transformFn);
+      expect(destEmpty).to.eql(emptyExtent);
+
+      const extentDeltaXNeg = [45, 67, 44, 78];
+      const destDeltaXNeg = _ol_extent_.applyTransform(
+        extentDeltaXNeg,
+        transformFn
+      );
+      expect(destDeltaXNeg).to.eql(emptyExtent);
+
+      const extentDeltaYNeg = [11, 67, 44, 66];
+      const destDeltaYNeg = _ol_extent_.applyTransform(
+        extentDeltaYNeg,
+        transformFn
+      );
+      expect(destDeltaYNeg).to.eql(emptyExtent);
+    });
+
     it('does transform', function () {
       const transformFn = getTransform('EPSG:4326', 'EPSG:3857');
       const sourceExtent = [-15, -30, 45, 60];
@@ -797,6 +818,21 @@ describe('ol/extent.js', function () {
       expect(destinationExtent[3]).to.roughlyEqual(8399737.889818361, 1e-8);
     });
 
+    it('does not treat a single point as empty', function () {
+      const transformFn = getTransform('EPSG:4326', 'EPSG:3857');
+      const sourceExtent = _ol_extent_.boundingExtent([[45, 60]]);
+      const destinationExtent = _ol_extent_.applyTransform(
+        sourceExtent,
+        transformFn
+      );
+      expect(destinationExtent).not.to.be(undefined);
+      expect(destinationExtent).not.to.be(null);
+      expect(destinationExtent[0]).to.roughlyEqual(5009377.085697311, 1e-9);
+      expect(destinationExtent[2]).to.eql(destinationExtent[0]);
+      expect(destinationExtent[1]).to.roughlyEqual(8399737.889818361, 1e-8);
+      expect(destinationExtent[3]).to.eql(destinationExtent[1]);
+    });
+
     it('takes arbitrary function', function () {
       const transformFn = function (input, output, opt_dimension) {
         const dimension = opt_dimension !== undefined ? opt_dimension : 2;